java elasticsearch 实现以图搜图效果

这篇具有很好参考价值的文章主要介绍了java elasticsearch 实现以图搜图效果。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

实现效果如下:
java elasticsearch 实现以图搜图效果

一、相关环境

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

二、引入算法

此算法是使用pytorch中resnet50模型计算图片的张量,数据存入elasticsearch中,匹配数据正弦值大小
将下面链接中的算法下载后即可,放入 D:/test/ 文件夹
无需配置相关算法环境
算法下载地址

三、创建表和索引

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

1、mysq中创建file_vector表

java elasticsearch 实现以图搜图效果

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 ,也可自行更改

//获取图片正弦值
    @Override
    public 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[]>() {
                @Override
                public 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;
                }

                @Override
                public 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类型的文件,则改为通过程序同步

 @Override
    public 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 client
            RestClient restClient = RestClient.builder(
                    new HttpHost(ES_IP, ES_PORT)).build();

            // Create the transport with a Jackson mapper
            ElasticsearchTransport transport = new RestClientTransport(
                    restClient, new JacksonJsonpMapper());

            // And create the API client
            ElasticsearchClient client = new ElasticsearchClient(transport);

            return client;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

九、演示效果

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

十、后续可优化点

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

到了这里,关于java elasticsearch 实现以图搜图效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Pytorch基于VGG cosine similarity实现简单的以图搜图(图像检索)

    代码如下: 上述代码的核心思想类似于感知损失(Perceptual Loss),利用VGG提取图像的多级特征,从而比较两张图像之间的相似性。区别在于Perceptual Loss中一般使用MAE,MSE比较特征的距离,而这里的代码使用余弦相似度。 一个例子如下,给定一张狸花的图像(query)如下: 我们希望

    2024年02月13日
    浏览(41)
  • 某某88以图搜图

    声明:本文仅限学习交流使用,禁止用于非法用途、商业活动等。否则后果自负。如有侵权,请告知删除,谢谢!本教程也没有专门针对某个网站而编写,单纯的技术研究 cnVub29iaHR0cHM6Ly9zLjE2ODguY29tL3lvdXl1YW4vaW5kZXguaHRtPw== 1.对应接口: 2.难点: 1.sign从直观来看是32位,所以我们可

    2024年02月15日
    浏览(37)
  • 使用火山云搜索ESCloud服务构建图文检索应用(以文搜图/以图搜图)

    图文检索在生活中具有广泛的应用,常见的图片检索包括基于文本内容搜索和基于图片内容搜索。用户通过输入文字描述或上传图片就可以在海量的图片库中快速找到同款或者相似图片,这种搜索方式被广泛应用于电商、广告、设计以及搜索引擎等热门领域。 本文 基于 火山

    2024年02月14日
    浏览(38)
  • 图片搜索引擎网站大全,以图搜图网站

    当我们需要搜索一些图片的时候使用图片搜索引擎网站可以帮我们更快地找到自己需要的图片,那么有哪些图片搜索引擎网站可以搜索图片呢?下面小编就来和大家分享几个以图搜图的网站。 1.百度图片搜索引擎网站 百度是最大的中文搜索引擎,百度的图片搜索以中文网站的

    2024年02月07日
    浏览(73)
  • 【milvus】向量数据库,用来做以图搜图+人脸识别的特征向量

    ref:https://milvus.io/docs 第一次装东西,要把遇到的问题和成功经验都记录下来。 1.Download the YAML file 看一下下载下来的是什么东西 Start Milvus In the same directory as the docker-compose.yml file, start up Milvus by running: 报错则需要安装docker-compose了 下载最新版的docker-compose 文件 添加可执行权限

    2024年02月16日
    浏览(43)
  • OpenCV #以图搜图:均值哈希算法(Average Hash Algorithm)原理与实验

    均值哈希算法(Average Hash Algorithm,简称aHash) 是哈希算法的一种,主要用来做相似图片的搜索工作。   均值哈希算法(aHash)首先将原图像缩小成一个固定大小的像素图像,然后将图像转换为灰度图像,通过缩小图像的每个像素与平均灰度值的比较,生成一组哈希值。最后,

    2024年02月08日
    浏览(42)
  • OpenCV #以图搜图:感知哈希算法(Perceptual hash algorithm)的原理与实验

    感知哈希算法(Perceptual Hash Algorithm,简称pHash) 是哈希算法的一种,主要可以用来做以图搜索/相似图片搜索工作。   感知哈希算法(pHash)首先将原图像缩小成一个固定大小的像素图像,然后将图像转换为灰度图像,通过使用离散余弦变换(DCT)来获取频域信息。然后,根

    2024年02月05日
    浏览(58)
  • Elasticsearch 8.X “图搜图”实战

    \\\"图搜图\\\"指的是通过图像搜索的一种方法,用户可以通过上传一张图片,搜索引擎会返回类似或者相关的图片结果。这种搜索方式不需要用户输入文字,而是通过比较图片的视觉信息来找到相似或相关的图片。这项技术在许多不同的应用中都很有用,如找到相同或相似的图片

    2024年02月12日
    浏览(36)
  • Elasticsearch 8.X进阶搜索之“图搜图”实战

    \\\"图搜图\\\"指的是通过图像搜索的一种方法,用户可以通过上传一张图片,搜索引擎会返回类似或者相关的图片结果。这种搜索方式不需要用户输入文字,而是通过比较图片的视觉信息来找到相似或相关的图片。这项技术在许多不同的应用中都很有用,如找到相同或相似的图片

    2024年02月03日
    浏览(31)
  • Elasticsearch实现Mysql的Like效果

    在Mysql数据库中,模糊搜索通常使用LIKE。然而,随着数据量的不断增加,Mysql在处理模糊搜索时可能面临性能瓶颈。因此,引入Elasticsearch作为搜索引擎,以提高搜索性能和用户体验成为一种合理的选择。 在ES中,影响搜索结果的因素多种多样,包括分词器、Match搜索、

    2024年02月19日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包