Elasticsearch:运用向量搜索通过图像搜索找到你的小狗

news2024/11/28 14:36:22

作者:ALEX SALGADO

你是否曾经遇到过这样的情况:你在街上发现了一只丢失的小狗,但不知道它是否有主人? 了解如何使用向量搜索或图像搜索来做到这一点。

通过图像搜索找到你的小狗

您是否曾经遇到过这样的情况:你在街上发现了一只丢失的小狗,但不知道它是否有主人? 在 Elasticsearch 中通过图像处理使用向量搜索,此任务可以像漫画一样简单。

想象一下这个场景:在一个喧闹的下午,路易吉,一只活泼的小狗,在 Elastic 周围散步时不小心从皮带上滑落,发现自己独自在繁忙的街道上徘徊。 绝望的主人正在各个角落寻找他,用充满希望和焦虑的声音呼唤着他的名字。 与此同时,在城市的某个地方,一位细心的人注意到这只小狗表情茫然,决定提供帮助。 很快,他们给路易吉拍了一张照片,并利用所在公司的向量图像搜索技术,开始在数据库中进行搜索,希望能找到有关这只小逃亡者主人的线索。

如果你想在阅读时跟踪并执行代码,请访问在 Jupyter Notebook (Google Collab) 上运行的文件 Python 代码。

架构

我们将使用 Jupyter Notebook 来解决这个问题。 首先,我们下载要注册的小狗的图像,然后安装必要的软件包。

注意:要实现此示例,我们需要在使用图像数据填充向量数据库之前在 Elasticsearch 中创建索引。

  • 首先部署 Elasticsearch(我们为你提供 14 天的免费试用期)。
  • 在此过程中,请记住存储要在 Python 代码中使用的凭据(用户名、密码)。
  • 为简单起见,我们将使用在 Jupyter Notebook (Google Colab) 上运行的 Python 代码。

下载代码 zip 文件并安装必要的软件包

!git clone https://github.com/salgado/image-search-01.git
!pip -q install Pillow sentence_transformers elasticsearch

让我们创建 4 个类来帮助我们完成这项任务,它们是:

  • Util 类:负责处理前期任务和 Elasticsearch 索引维护。
  • Dog 类:负责存储我们小狗的属性。
  • DogRepository 类:负责数据持久化任务。
  • DogService 类:它将成为我们的服务层。

Util class

Util 类提供了用于管理 Elasticsearch 索引的实用方法,例如创建和删除索引。

方法

  • create_index():在 Elasticsearch 中创建一个新索引。
  • delete_index():从 Elasticsearch 中删除现有索引。
### Util class
from elasticsearch import Elasticsearch, exceptions as es_exceptions
import getpass

class Util:
    @staticmethod
    def get_index_name():
      return "dog-image-index"

    @staticmethod
    def get_connection():
        es_cloud_id = getpass.getpass('Enter Elastic Cloud ID:  ')
        es_user = getpass.getpass('Enter cluster username:  ')
        es_pass = getpass.getpass('Enter cluster password:  ')

        es = Elasticsearch(cloud_id=es_cloud_id,
                          basic_auth=(es_user, es_pass)
                          )
        es.info() # should return cluster info
        return es


    @staticmethod
    def create_index(es: Elasticsearch, index_name: str):
        # Specify index configuration
        index_config = {
          "settings": {
            "index.refresh_interval": "5s",
            "number_of_shards": 1
          },
          "mappings": {
            "properties": {
              "image_embedding": {
                "type": "dense_vector",
                "dims": 512,
                "index": True,
                "similarity": "cosine"
              },
              "dog_id": {
                "type": "keyword"
              },
              "breed": {
                "type" : "keyword"
              },
              "image_path" : {
                "type" : "keyword"
              },
              "owner_name" : {
                "type" : "keyword"
              },
              "exif" : {
                "properties" : {
                  "location": {
                    "type": "geo_point"
                  },
                  "date": {
                    "type": "date"
                  }
                }
              }
            }
          }
        }

        # Create index
        if not es.indices.exists(index=index_name):
            index_creation = es.indices.create(index=index_name, ignore=400, body=index_config)
            print("index created: ", index_creation)
        else:
            print("Index  already exists.")


    @staticmethod
    def delete_index(es: Elasticsearch, index_name: str):
        # delete index
        es.indices.delete(index=index_name, ignore_unavailable=True)

如果你是自构建的集群,你可以参考文章 “Elasticsearch:关于在 Python 中使用 Elasticsearch 你需要知道的一切 - 8.x” 来了解如何使用客户端来连接 Elasticsearch 集群。

Dog class

Dog 类代表一只狗及其属性,例如 ID、图像路径、品种、所有者姓名和图像嵌入。

属性

  • dog_id:狗的 ID。
  • image_path:狗图像的路径。
  • breed:狗的品种。
  • owner_name:狗的主人的名字。
  • image_embedding:狗的图像嵌入。

方法:

  • __init__():初始化一个新的 Dog 对象。
  • generate_embedding():生成狗的图像嵌入。
  • to_dict():将 Dog 对象转换为字典。
import os
from sentence_transformers import SentenceTransformer
from PIL import Image

# domain class
class Dog:
    model = SentenceTransformer('clip-ViT-B-32')

    def __init__(self, dog_id, image_path, breed, owner_name):
        self.dog_id = dog_id
        self.image_path = image_path
        self.breed = breed
        self.image_embedding = None
        self.owner_name = owner_name

    @staticmethod
    def get_embedding(image_path: str):
        temp_image = Image.open(image_path)
        return Dog.model.encode(temp_image)

    def generate_embedding(self):
        self.image_embedding = Dog.get_embedding(self.image_path)

    def __repr__(self):
        return (f"Image(dog_id={self.dog_id}, image_path={self.image_path}, "
                f"breed={self.breed}, image_embedding={self.image_embedding}, "
                f"owner_name={self.owner_name})")

    def to_dict(self):
        return {
            'dog_id': self.dog_id,
            'image_path': self.image_path,
            'breed': self.breed,
            'image_embedding': self.image_embedding,
            'owner_name': self.owner_name
        }

DogRepository class

DogRepository 类提供了从 Elasticsearch 保存和检索狗数据的方法。

方法

  • insert():将一条新狗插入 Elasticsearch。
  • bulk_insert():将多条狗批量插入到Elasticsearch中。
  • search_by_image():通过图像搜索相似的狗。
from typing import List, Dict
# persistence layer
class DogRepository:
    def __init__(self, es_client: Elasticsearch, index_name: str = "dog-image-index"):
        self.es_client = es_client
        self._index_name = index_name
        Util.create_index(es_client, index_name)

    def insert(self, dog: Dog):
        dog.generate_embedding()
        document = dog.__dict__
        self.es_client.index(index=self._index_name, document=document)

    def bulk_insert(self, dogs: List[Dog]):
        operations = []
        for dog in dogs:
            operations.append({"index": {"_index": self._index_name}})
            operations.append(dog.__dict__)
        self.es_client.bulk(body=operations)

    def search_by_image(self, image_embedding: List[float]):
      field_key = "image_embedding"

      knn = {
          "field": field_key,
          "k": 2,
          "num_candidates": 100,
          "query_vector": image_embedding,
          "boost": 100
      }

      # The fields to retrieve from the matching documents
      fields = ["dog_id", "breed", "owner_name","image_path", "image_embedding"]

      try:
          resp = self.es_client.search(
              index=self._index_name,
              body={
                  "knn": knn,
                  "_source": fields
              },
              size=1
          )
          # Return the search results
          return resp
      except Exception as e:
          print(f"An error occurred: {e}")
          return {}

DogService Class

DogService 类提供管理狗数据的业务逻辑,例如插入和搜索狗。

方法

  • insert_dog():将一条新狗插入 Elasticsearch。
  • search_dogs_by_image():通过图像搜索相似的狗。

from typing import List, Dict
# persistence layer
class DogRepository:
    def __init__(self, es_client: Elasticsearch, index_name: str = "dog-image-index"):
        self.es_client = es_client
        self._index_name = index_name
        Util.create_index(es_client, index_name)

    def insert(self, dog: Dog):
        dog.generate_embedding()
        document = dog.__dict__
        self.es_client.index(index=self._index_name, document=document)

    def bulk_insert(self, dogs: List[Dog]):
        operations = []
        for dog in dogs:
            operations.append({"index": {"_index": self._index_name}})
            operations.append(dog.__dict__)
        self.es_client.bulk(body=operations)

    def search_by_image(self, image_embedding: List[float]):
      field_key = "image_embedding"

      knn = {
          "field": field_key,
          "k": 2,
          "num_candidates": 100,
          "query_vector": image_embedding,
          "boost": 100
      }

      # The fields to retrieve from the matching documents
      fields = ["dog_id", "breed", "owner_name","image_path", "image_embedding"]

      try:
          resp = self.es_client.search(
              index=self._index_name,
              body={
                  "knn": knn,
                  "_source": fields
              },
              size=1
          )
          # Return the search results
          return resp
      except Exception as e:
          print(f"An error occurred: {e}")
          return {}

上面介绍的类(classes)为构建狗数据管理系统奠定了坚实的基础。 Util 类提供了用于管理 Elasticsearch 索引的实用方法。 Dog 类代表狗的属性。 DogRepository 类提供了从 Elasticsearch 保存和检索狗数据的方法。 DogService 类提供了高效的狗数据管理的业务逻辑。

主要代码

我们的代码基本上有两个主要流程或阶段:

  • 使用基本信息和图像注册狗。
  • 使用新图像执行搜索以在向量数据库中查找狗。

第一阶段:注册小狗

为了存储有关 Luigi 和其他公司的小狗的信息,我们将使用 Dog 类。

为此,我们对序列进行如下的编程:

开始为小狗登记


# Start a connection
es_db = Util.get_connection()
Util.delete_index(es_db, Util.get_index_name())

# Register one dog
dog_repo = DogRepository(es_db, Util.get_index_name())
dog_service = DogService(dog_repo)

# Visualize the inserted Dog
from IPython.display import display
from IPython.display import Image as ShowImage

filename = "/content/image-search-01/dataset/dogs/Luigi.png"
display(ShowImage(filename=filename, width=300, height=300))

输出:

登记 Luigi

dog = Dog('Luigi', filename, 'Jack Russel/Rat Terrier', 'Ully')

dog_service.register_dog(dog)

登记所有其他小狗

import json

# JSON data
data = '''
{
  "dogs": [
    {"dog_id": "Buddy", "image_path": "", "breed": "Labrador Retriever", "owner_name": "Berlin Red"},
    {"dog_id": "Bella", "image_path": "", "breed": "German Shepherd", "owner_name": "Tokyo Blue"},
    {"dog_id": "Charlie", "image_path": "", "breed": "Golden Retriever", "owner_name": "Paris Green"},
    {"dog_id": "Bigu", "image_path": "", "breed": "Beagle", "owner_name": "Lisbon Yellow"},
    {"dog_id": "Max", "image_path": "", "breed": "Bulldog", "owner_name": "Canberra Purple"},
    {"dog_id": "Luna", "image_path": "", "breed": "Poodle", "owner_name": "Wellington Brown"},
    {"dog_id": "Milo", "image_path": "", "breed": "Rottweiler", "owner_name": "Hanoi Orange"},
    {"dog_id": "Ruby", "image_path": "", "breed": "Boxer", "owner_name": "Ottawa Pink"},
    {"dog_id": "Oscar", "image_path": "", "breed": "Dachshund", "owner_name": "Kabul Black"},
    {"dog_id": "Zoe", "image_path": "", "breed": "Siberian Husky", "owner_name": "Cairo White"}
  ]
}
'''

# Convert JSON string to Python dictionary
dogs_data = json.loads(data)

# Traverse the list and print dog_id of each dog
image_dogs = "/content/image-search-01/dataset/dogs/"
for dog_info in dogs_data["dogs"]: 
    dog = Dog(dog_info["dog_id"], image_dogs + dog_info["dog_id"] + ".png" , dog_info["breed"], dog_info["owner_name"])
    dog_service.register_dog(dog)

可视化新狗

# visualize new dogs
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import math

image_dogs = "/content/image-search-01/dataset/dogs/"
num_dogs = len(dogs_data["dogs"])

cols = int(math.sqrt(num_dogs))
rows = int(math.ceil(num_dogs / cols))

# Configurar o tamanho da figura
plt.figure(figsize=(5, 5))

# Loop para exibir as imagens dos cães
for i, dog_info in enumerate(dogs_data["dogs"]): 
    filename = image_dogs + dog_info["dog_id"] + ".png"
    img = mpimg.imread(filename)
    
    plt.subplot(rows, cols, i+1)  # (número de linhas, número de colunas, índice do subplot)
    plt.imshow(img)
    plt.axis('off')

plt.show()

输出:

第二阶段:寻找丢失的狗

现在我们已经登记了所有小狗,让我们进行搜索。 我们的开发人员拍了这张丢失小狗的照片。

filename = "/content/image-search-01/dataset/lost-dogs/lost_dog1.png"
display(ShowImage(filename=filename, width=300, height=300))

输出:

看看我们能找到这只可爱的小狗的主人吗?

# find dog by image
result = dog_service.find_dog_by_image(filename)

获取结果

让我们看看我们发现了什么......

filename = result['hits']['hits'][0]['_source']['image_path']
display(ShowImage(filename=filename, width=300, height=300))

输出:

瞧! 我们找到了!!!

但谁将是所有者和他们的名字?

# Print credentials
print(result['hits']['hits'][0]['_source']['dog_id'])
print(result['hits']['hits'][0]['_source']['breed'])
print(result['hits']['hits'][0]['_source']['owner_name'])

输出:

  • Luigi
  • Jack Russel/Rat Terrier
  • Ully

好结局

我们找到了路易吉!!! 我们通知 Ully 吧。

filename = "/content/image-search-01/dataset/lost-dogs/Ully.png"
display(ShowImage(filename=filename, width=300, height=300))

输出:

很快,Ully 和 Luigi 就团聚了。 小狗高兴地摇着尾巴,Ully 紧紧地抱住它,保证再也不会让它离开她的视线。 他们经历了一阵情感旋风,但现在他们在一起了,这才是最重要的。 就这样,Ully 和 Luigi 心中充满了爱和欢乐,从此幸福地生活在一起。

结论

在这篇博文中,我们探索了如何使用 Elasticsearch 通过向量搜索来寻找丢失的小狗。 我们演示了如何生成狗的图像嵌入,在 Elasticsearch 中对其进行索引,然后使用查询图像搜索相似的狗。 该技术可用于寻找丢失的宠物,以及识别图像中其他感兴趣的物体。

向量搜索是一个强大的工具,可用于多种应用。 它特别适合需要根据外观搜索相似对象的任务,例如图像检索和对象识别。

我们希望这篇博文能够提供丰富的信息,并且你会发现我们讨论的技术对你自己的项目很有用。

原文:Finding your puppy with Image Search — Elastic Search Labs

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

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

相关文章

【自留地】前端 - uniapp - Vue - React - Flutter

uniapp uniapp自用速查表 - 我的常用组件 uniapp自用速查表 - 我的常用组件_uniapp static/customicons.css-CSDN博客文章浏览阅读1.8k次。uniapp项目登录退出、全局变量与状态、本地存储、Tabbar标签栏、顶部导航栏、下拉刷新、触底刷新、Ajax交互、内置组件样式修改、自定义…

2018年五一杯数学建模C题江苏省本科教育质量综合评价解题全过程文档及程序

2019年五一杯数学建模 C题 江苏省本科教育质量综合评价 原题再现 随着中国的改革开放,国家的综合实力不断增强,中国高等教育发展整体已进入世界中上水平。作为一个教育大省,江苏省的本科教育发展在全国名列前茅,而江苏省13个地级…

Linux:进程替换和知识整合

文章目录 进程程序替换替换原理进程替换的理解 环境变量与进程替换命令行解释器实现逻辑 进程程序替换 前面已经学习了子进程的创建,但是子进程的创建不管怎么说,都是父进程代码的一部分,那么实际上如果想要子进程执行新的程序呢&#xff1f…

2023年中国脑电仿生电刺激仪发展趋势分析:智能化、精准化、使用感不断提高[图]

脑电仿生电刺激仪是一种通过直接数字频率合成技术合成脑电仿真低频生物电流,通过粘贴于两耳侧乳突、太阳穴或风池穴部位表皮的电极,用仿生物电自颅外无创伤地穿透颅骨屏障刺激小脑顶核区的电疗设备。此电流刺激可启动颅脑固有神经保护机制,改…

【MATLAB】史上最全的9种数据拟合算法全家桶

有意向获取代码,请转文末观看代码获取方式~ 大家吃一顿火锅的价格便可以拥有9种数据拟合算法,绝对不亏,知识付费是现今时代的趋势,而且都是我精心制作的教程,有问题可随时反馈~也可单独获取某一算法的代码&#xff08…

Odoo 15开发手册第六章 模型 - 结构化应用数据

本章我们更进一步学习模型层,以及如何使用模型来设计支撑应用的数据结构。我们会探讨可用的模型类型,以及在使用这些类型时如何定义强制进行数据验证的约束。 模型由支持不同数据类型的数据字段组成,一些字段类型支持定义模型间的关联。对于…

delphi电子处方流转 sm2 sm4(医院)

【delphi电子处方流转(医院) sm2 sm4】支持 就诊登记、电子处方上传预核验、处方处方医保电子签名、电子处方上传、电子处方撤销、电子处方信息查询、电子处方审核结果查询、电子处方取药结果查询、电子处方药品目录查询等功能。技术交流Q 648437169 下载链接:http…

4. hdfs高可用集群搭建

简介 前面把hadoop机器已经准备好了,zk集群搭建好了,本本就是开始搭建hdfs环境 hadoop环境准备 创建hadoop用户 三台机器都创建hadoop用户 useradd hadoop -d /home/hadoop echo "1q1w1e1r" | passwd --stdin hadoophadoop用户相互免密登…

语义检索系统【全】:基于milvus语义检索系统指令全流程-快速部署版

搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术细节以及项目实战(含码源) 专栏详细介绍:搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术…

Linux操作系统基础 – 正则表达式快速入门

Linux操作系统基础 – 正则表达式快速入门 Linux Operating System Essentials - Introduction to Regular Expressions 通常在计算机科学领域,正则表达式被解释为对字符串操作的一种逻辑公式,即用事先定义好的特定字符及其组合组成所谓的“规则字符串”…

SpringBoot使用DevTools实现后端热部署

📑前言 本文主要SpringBoot通过DevTools实现热部署的文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是青衿🥇 ☁️博客首页:CSDN主页放风讲故事 🌄每日一句&…

GZ038 物联网应用开发赛题第10套

2023年全国职业院校技能大赛 高职组 物联网应用开发 任 务 书 (第10套卷) 工位号:______________ 第一部分 竞赛须知 一、竞赛要求 1、正确使用工具,操作安全规范; 2、竞赛过程中如有异议,可向现场考…

ai剪辑矩阵系统源码+无人直播系统源码技术开发

开发AI剪辑矩阵系统和无人直播系统源码,需要以下步骤: 1. 市场调研:了解市场需求和竞品情况,明确系统的功能和特点。 2. 系统设计:设计系统的整体架构和功能模块,包括视频剪辑、直播推流、实时互动、数据分…

【Linux】Ubuntu16.04下安装python高版本--源码安装

Ubuntu16.04下完美安装python高版本及对应版本的pip 方法一:直接用命令安装python3.6(但我没安装成功) 好像是因为Ubuntu16.04的软件仓库(源)中python的最高版本就是python3.5,所以无法直接用apt来安装 #方法一 sudo…

学习c#的第十四天

目录 C# 接口(Interface) 接口的特点 定义接口 接口继承 接口和抽象类的区别 C# 命名空间(Namespace) using 关键字 定义命名空间 嵌套命名空间 C# 接口(Interface) 接口定义了所有类继承接口时应…

Linux系统进程——进程的退出、子进程退出的收集、孤儿进程

进程退出 进程退出主要分为两种:正常退出、异常退出 正常退出 正常退出分为以下几种: 1.main函数调用return 2.进程调用exit(),标准c库 3.进程调用 _exit() 或者 _Exit() ,属于系统调用 4.进程最后一个线程返回 5.最后一个线程调用pthrea…

asp.net网上书店管理系统VS开发sqlserver数据库web结构c#编程计算机网页源码项目

一、源码特点 asp.net网上书店管理系统 是一套完善的web设计管理系统,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 asp.net网上书店系统1 二、功能介绍 本系统使用Microsoft Visual Studio 2019为开发工具,SQL Server为…

影响气膜建筑坍塌的原因

气膜建筑以其轻盈、透光、环保等特性,逐渐在建筑领域崭露头角。然而,这种建筑形式并非没有缺陷,其安全性与稳定性直接影响到建筑物的使用寿命和人员安全。 一、结构设计不合理 气膜建筑的结构设计是影响其稳定性的关键因素。良好的结构设计能…

LeetCode(24)文本左右对齐【数组/字符串】【困难】

目录 1.题目2.答案3.提交结果截图 链接: 文本左右对齐 1.题目 给定一个单词数组 words 和一个长度 maxWidth ,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单…

delphi电子处方流转 sm2 sm4(药店)

【delphi电子处方流转(药店)】支持 处方下载、处方核验、处方审核、药品销售出库明细上传、药品销售出库明细撤销等功能。技术交流Q 648437169 下载链接:https://download.csdn.net/download/liushenglin123/88543771