前言:
现在需要用java+elasticsearch的方式实现以图搜图的效果,效果如下:
相关文章:https://blog.csdn.net/m0_52640724/article/details/129357847

实现效果如下:

目录

  • 一、相关环境
  • 二、引入算法
  • 三、创建表和索引
    • 1、mysq中创建file_vector表
    • 2、创建es索引
  • 四、java项目引入依赖
  • 五、调用算法
  • 六、预处理文件数据
  • 七、同步数据到es
  • 八、查询数据
  • 九、演示效果
  • 十、后续可优化点

一、相关环境

java:jdk11
elasticsearch:7.17.3
windows:win10
linux:centos7.9

二、引入算法

此算法是使用pytorch计算图片的正弦值,匹配图片正弦值的内容
将下面链接中的算法下载后即可,放入 D:/test/ 文件夹
无需配置相关算法环境
算法下载地址

三、创建表和索引

避免重复生成内容,将算法生成的正弦值存入mysql表中,设置mysql和es数据同步

1、mysq中创建file_vector表

2、创建es索引

PUT /file_vector
{"mappings": {"properties": {"vectorList": {"type": "dense_vector","dims": 1024},"url" : {"type" : "keyword"},"fileId": {"type": "keyword"}}}
}

四、java项目引入依赖

本项目使用的是maven,直接在pom文件中引入依赖即可

注意:由于环境不一致,在本地开发过程中引入的是windows版本依赖,在linux环境中引入的是linux版本依赖,如果linux为centos8以上,似乎windows版本依赖也可行

<!--elasticsearch依赖     开始--><dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>7.17.3</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.3</version></dependency><dependency><groupId>jakarta.json</groupId><artifactId>jakarta.json-api</artifactId><version>2.0.1</version></dependency><!--elasticsearch依赖     结束--><!--提取图片正弦值依赖开始   windows环境依赖-->
<!--        <dependency>-->
<!--            <groupId>ai.djl.pytorch</groupId>-->
<!--            <artifactId>pytorch-engine</artifactId>-->
<!--            <version>0.19.0</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>ai.djl.pytorch</groupId>-->
<!--            <artifactId>pytorch-native-cpu</artifactId>-->
<!--            <version>1.10.0</version>-->
<!--            <scope>runtime</scope>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>ai.djl.pytorch</groupId>-->
<!--            <artifactId>pytorch-jni</artifactId>-->
<!--            <version>1.10.0-0.19.0</version>-->
<!--        </dependency>--><!--提取图片正弦值依赖结束  windows环境依赖 --><!--提取图片正弦值依赖开始   linux环境依赖--><dependency><groupId>ai.djl.pytorch</groupId><artifactId>pytorch-engine</artifactId><version>0.16.0</version></dependency><dependency><groupId>ai.djl.pytorch</groupId><artifactId>pytorch-native-cpu-precxx11</artifactId><classifier>linux-x86_64</classifier><version>1.9.1</version><scope>runtime</scope></dependency><dependency><groupId>ai.djl.pytorch</groupId><artifactId>pytorch-jni</artifactId><version>1.9.1-0.16.0</version><scope>runtime</scope></dependency><!--提取图片正弦值依赖结束 linux环境依赖 -->

五、调用算法

将第二步中的算法放入对应的文件夹中
在下面代码中,windows版本下算法路径为 D:/test/faceModel.pt ,也可自行更改

//获取图片正弦值@Overridepublic Predictor<Image, float[]> getVectorData() {Model model; //模型Predictor<Image, float[]> predictor; //predictor.predict(input)相当于python中model(input)int IMAGE_SIZE = 224;try {model = Model.newInstance("faceModel");//这里的model.pt是上面代码展示的那种方式保存的
//            model.load(FileInfoServiceImpl.class.getClassLoader().getResourceAsStream("faceModel.pt"));model.load(new FileInputStream("D:/test/faceModel.pt"));
//            model.load(new FileInputStream("/usr/local/dm/algorithm/faceModel.pt"));Transform resize = new Resize(IMAGE_SIZE);Transform toTensor = new ToTensor();Transform normalize = new Normalize(new float[]{0.485f, 0.456f, 0.406f}, new float[]{0.229f, 0.224f, 0.225f});//Translator处理输入Image转为tensor、输出转为float[]Translator<Image, float[]> translator = new Translator<Image, float[]>() {@Overridepublic NDList processInput(TranslatorContext ctx, Image input) throws Exception {NDManager ndManager = ctx.getNDManager();System.out.println("input: " + input.getWidth() + ", " + input.getHeight());NDArray transform = normalize.transform(toTensor.transform(resize.transform(input.toNDArray(ndManager))));
//                    System.out.println(transform.getShape());NDList list = new NDList();list.add(transform);return list;}@Overridepublic float[] processOutput(TranslatorContext ctx, NDList ndList) throws Exception {return ndList.get(0).toFloatArray();}};predictor = new Predictor<>(model, translator, Device.cpu(), true);return predictor;} catch (Exception e) {e.printStackTrace();}return null;}

六、预处理文件数据

将 D:/test/photo/ 文件夹中放入图片,调用接口批量生成图片的正弦值存入表中

public void addFileVector111() {try {File file = new File("D:/test/photo/");for (File listFile : file.listFiles()) {InputStream inputStream = new FileInputStream("D:/test/photo/" + listFile.getName());Predictor<Image, float[]> vectorData = getVectorData();float[] vector = vectorData.predict(ImageFactory.getInstance().fromInputStream(inputStream));if (vector == null) {log.error("生成正弦值内容失败");continue;}Gson gson = new Gson();String s = gson.toJson(vector);String newSub = s.substring(1, s.length() - 1);//存储fileVector表FileVector f = new FileVector();f.setVectorList(newSub);f.setUrl(listFile.getAbsolutePath());f.setStatus("1");int i = fileVectorDao.insertSelective(f);if (i <= 0) continue;}} catch (Exception e) {e.printStackTrace();log.error("添加图片正弦值失败" + e);}}

七、同步数据到es

原本mysql数据同步到es用的是canal,似乎canal无法传输text类型的文件,则改为通过程序同步

 @Overridepublic ApiResult addEsFileVectorList() {ElasticsearchClient esClient = null;Long sqlLimitNum = 1000L;Boolean flag = true;try {long beginTime = System.currentTimeMillis();Integer successNum = 0;Long beginFileVectorId = 0L;Long endFileVectorId = sqlLimitNum;while (flag) {List<FileVector> fileVectorList = fileVectorDao.selectFileVectorList(beginFileVectorId, sqlLimitNum);if (fileVectorList != null && fileVectorList.size() > 0) {BulkRequest.Builder br = new BulkRequest.Builder();List<Long> successFileVecIdList = new ArrayList<>();//成功的同步id记录for (FileVector f : fileVectorList) {String[] strArray = f.getVectorList().split(",");Float[] floatArray = Arrays.stream(strArray).map(Float::parseFloat).toArray(Float[]::new);//存储es数据Map<String, Object> jsonMap = new HashMap<>();jsonMap.put("fileId", f.getFileId());jsonMap.put("vectorList", floatArray);jsonMap.put("url", f.getUrl());br.operations(op -> op.index(idx -> idx.index("file_vector").id(f.getFileVectorId().toString()).document(jsonMap)));successFileVecIdList.add(f.getFileVectorId());}if (successFileVecIdList != null && successFileVecIdList.size() > 0) {esClient = this.getEsClient();BulkResponse bulk = esClient.bulk(br.build());if (bulk.errors()) {System.out.println("有部分数据操作失败");for (BulkResponseItem item : bulk.items()) {if (item.error() != null) {//如果失败需要将失败的id保存Long failFileVectorId = Long.valueOf(String.valueOf(item.id()));successFileVecIdList.remove(failFileVectorId);}}}}//修改file_vector表中同步状态if (successFileVecIdList != null && successFileVecIdList.size() > 0)fileVectorDao.updateStatusByFileIdList(successFileVecIdList, "0");successNum += successFileVecIdList.size();beginFileVectorId = endFileVectorId + 1;endFileVectorId = endFileVectorId + sqlLimitNum;} else {flag = false;}}long endTime = System.currentTimeMillis();System.out.println("用时:" + (endTime - beginTime) + "ms");return ApiResult.success("同步成功,共执行" + successNum + "条记录");} catch (Exception e) {e.printStackTrace();log.error("批量同步es_file_vector失败" + e);} finally {try {esClient._transport().close();} catch (IOException e) {e.printStackTrace();}}return ApiResult.error("同步失败");}

八、查询数据

接收一张图片,调用算法获取图片正弦值,调用es获取匹配数据
可自行设置匹配图片匹配阈值,下面代码中设置的是0.8

public static List<SearchResult> search1(InputStream input) {ElasticsearchClient client = null;try {float[] vector = getVectorList().predict(ImageFactory.getInstance().fromInputStream(input));System.out.println(Arrays.toString(vector));// 连接Elasticsearch服务器client = getEsClient();Script.Builder script = new Script.Builder();script.inline(_1 -> _1.lang("painless").source("cosineSimilarity(params.queryVector, doc['vectorList'])").params("queryVector", JsonData.of(vector)));FunctionScoreQuery.Builder funQueryBuilder = new FunctionScoreQuery.Builder();funQueryBuilder.query(_1 -> _1.matchAll(_2 -> _2));funQueryBuilder.functions(_1 -> _1.scriptScore(_2 -> _2.script(script.build())));SearchResponse<Map> search = client.search(_1 -> _1.index("file_vector").query(funQueryBuilder.build()._toQuery()).source(_2 -> _2.filter(_3 -> _3.excludes("vector"))).size(100).minScore(0.8)  //此处是设置返回匹配最低分数, Map.class);List<SearchResult> list = new ArrayList<>();List<Hit<Map>> hitsList = search.hits().hits();for (Hit<Map> mapHit : hitsList) {float score = mapHit.score().floatValue();String url = (String) mapHit.source().get("url");SearchResult aa = new SearchResult(url, score);list.add(aa);}return list;} catch (Exception e) {e.printStackTrace();} finally {try {client._transport().close();} catch (IOException e) {e.printStackTrace();}}return null;}//生成es连接private static ElasticsearchClient getEsClient() {try {//调用es有同步和异步之分,下列方法是同步阻塞调用// Create the low-level clientRestClient restClient = RestClient.builder(new HttpHost(ES_IP, ES_PORT)).build();// Create the transport with a Jackson mapperElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());// And create the API clientElasticsearchClient client = new ElasticsearchClient(transport);return client;} catch (Exception e) {e.printStackTrace();}return null;}

九、演示效果

通过设置不同的阈值,匹配的精确程度也不一样,如果设置阈值为0.9,只会返回构图完全一样的图片,设置为0.8,则会实现下图效果

十、后续可优化点

1、在上面的流程设计中,是通过java程序同步的es,java程序设置定时任务同步,时效性会比较差,mysql中无法存放float[]格式数据,看是否有其他方案提高同步时效性
2、图片阈值方面的设置还需要根据具体场景具体分析,阈值太低容易误读文件,阈值太高容易漏查文件
大家有什么好的解决方案欢迎留言探讨。

java elasticsearch 实现以图搜图效果相关推荐

  1. 使用elasticSearch搭建本地以图搜图服务

    使用elasticSearch的向量检索实现以图搜图 文章目录 使用elasticSearch的向量检索实现以图搜图 前言 快速开始 安装elasticsearch和kibana 创建索引 下载项目 ...

  2. Java+ElasticSearch+Pytorch实现以图搜图

    以图搜图,涉及两大功能:1.提取图像特征向量.2.相似向量检索. 第一个功能我通过编写pytorch模型并在java端借助djl调用实现,第二个功能通过elasticsearch7.6.2的dense ...

  3. Java调用Pytorch实现以图搜图(附源码)

    Java调用Pytorch实现以图搜图 设计技术栈: 1.ElasticSearch环境: 2.Python运行环境(如果事先没有pytorch模型时,可以用python脚本创建模型): 1.运行效果 ...

  4. towhee+elasticsearch实现本地以图搜图

    towhee-img-search towhee+elasticsearch实现本地以图搜图 github地址:https://github.com/xjhqre/towhee-img-search ...

  5. Google 以图搜图 - 相似图片搜索原理 - Java实现

    前阵子在阮一峰的博客上看到了这篇<相似图片搜索原理>博客,就有一种冲动要将这些原理实现出来了. Google "相似图片搜索":你可以用一张图片,搜索互联网上所有与它相 ...

  6. 以图搜图 - Google 相似图片搜索原理 - Java实现

    转自:http://blog.csdn.net/luohong722/article/details/7100058 前阵子在阮一峰的博客上看到了这篇<相似图片搜索原理>博客,就有一种冲动 ...

  7. Google 以图搜图 - 相似图片搜索原理 - Java实现 (转)

    前阵子在阮一峰的博客上看到了这篇<相似图片搜索原理>博客,就有一种冲动要将这些原理实现出来了. Google "相似图片搜索":你可以用一张图片,搜索互联网上所有与它相 ...

  8. Torch、Java、Milvus快速搭建以图搜图系统

    Torch.Java.Milvus快速搭建以图搜图系统 1 原理概述 以图搜图大致原理(口水话版) 以图搜图,即通过一张图片去匹配数据库中的图片,找到最相似的N张图.在我们普通的搜索系统中,文字匹配的 ...

  9. 推荐系统工程篇之搭建以图搜图服务

    基于内容的召回在推荐系统中是比较常见的召回策略,常见有基于用户或物品的标签召回或者基于用户的年龄,地域等召回,一般该策略的实现是基于开源软件 Elasticseach 实现的.虽然召回的结果都比较合理 ...

最新文章

  1. UCZProgressView CALayer旋转
  2. 四、HTTP响应报文格式
  3. 大型网站架构模式之三
  4. Subset-Sum Problem 子集和问题
  5. c语言和gtk实现的游戏,Ubuntu下用glade和GTK+开发C语言界面程序(一)
  6. sql优化之:数据库索引创建原则,or/in/union与索引优化,聚集索引/非聚集索引/联合索引/索引覆盖,MySQL冗余数据的三种方案,MySQL双主一致性架构优化(来源:架构师之路)
  7. display block 无法显示_display:inline-block产生的问题
  8. python之路---03 整型 bool 字符串 for循环
  9. java 列表展开方式_android列表控件实现展开、收缩功能
  10. java开发工具软件排行榜
  11. JS的typeof力所能及已经力所不及
  12. SpringBoot2.1.5(11)---目录文件结构讲解
  13. 如何在Mac系统清理 “其他” 中文件和数据
  14. Java关键字this详解
  15. 火焰检测的相关步骤及算法综述
  16. CD Linux启动盘,CDlinux硬盘启动制作方法。CDlinux硬盘怎样启动制作?
  17. 乾隆的太医留下来的民间偏方
  18. 清除input numer 点击样式
  19. 【Micro Project】怎样设置自动安排的起始时间
  20. 轻量级的肝脏与肝肿瘤 2.5D 分割网络阅读笔记

热门文章

  1. 一款适用于搭建内部培训平台的系统,开源了!
  2. linux系统万能播放器下载,360万能播放器Linux版
  3. 解决ImportError: /home/xxx/Software/anaconda3/lib/libstdc++.so.6: version `GLIBCXX_3.4.29‘ not found报错
  4. 学生必备的9款宝藏APP,不仅免费还巨好用
  5. git文件夹出现蓝色问号 解决方法
  6. 【收集】Http Content-Tpye 对照表大全
  7. 图说2016深度学习十大指数级增长
  8. Vue2实现响应式布局方案
  9. 鹦鹉螺,我在海边等着你(转)
  10. linux signal 11 问题,embedded-在Linux中qemu:uncaught target signal 11(分段错误)