Java【代码 16】Milvus向量库工具类和SeetaSDK获取人像向量和属性的工具类分享

Milvus向量库和SeetaSDK工具类分享

1.Milvus向量库工具类

Milvus的Maven依赖:

        <dependency>
            <groupId>io.milvus</groupId>
            <artifactId>milvus-sdk-java</artifactId>
            <version>2.1.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>log4j-slf4j-impl</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>

向量库的配置类:

@Data
@Component
@ConfigurationProperties(MilvusConfiguration.PREFIX)
public class MilvusConfiguration {
    public static final String PREFIX = "milvus-config";

    public String host;
    public int port;
    public String collectionName;

}

工具类主类:

@Slf4j
@Component
public class MilvusUtil {

    @Resource
    private MilvusConfiguration milvusConfiguration;

    private MilvusServiceClient milvusServiceClient;

    @PostConstruct
    private void connectToServer() {
        milvusServiceClient = new MilvusServiceClient(
                ConnectParam.newBuilder()
                        .withHost(milvusConfiguration.host)
                        .withPort(milvusConfiguration.port)
                        .build());
        // 加载数据
        LoadCollectionParam faceSearchNewLoad = LoadCollectionParam.newBuilder()
                .withCollectionName(milvusConfiguration.collectionName).build();
        R<RpcStatus> rpcStatusR = milvusServiceClient.loadCollection(faceSearchNewLoad);
        log.info("Milvus LoadCollection [{}]", rpcStatusR.getStatus() == 0 ? "Successful!" : "Failed!");
    }
}

主类里的数据入库方法:

    public int insertDataToMilvus(String id, String path, float[] feature) {
        List<InsertParam.Field> fields = new ArrayList<>();
        List<Float> featureList = new ArrayList<>(feature.length);
        for (float v : feature) {
            featureList.add(v);
        }
        fields.add(new InsertParam.Field("id", Collections.singletonList(id)));
        fields.add(new InsertParam.Field("image_path", Collections.singletonList(path)));
        fields.add(new InsertParam.Field("image_feature", Collections.singletonList(featureList)));
        InsertParam insertParam = InsertParam.newBuilder()
                .withCollectionName(milvusConfiguration.collectionName)
                //.withPartitionName("novel")
                .withFields(fields)
                .build();
        R<MutationResult> insert = milvusServiceClient.insert(insertParam);
        return insert.getStatus();
    }

主类类的数据查询方法:

  • 这里的topK没有进行参数化。
    public List<MilvusRes> searchImageByFeatureVector(float[] feature) {
        List<Float> featureList = new ArrayList<>(feature.length);
        for (float v : feature) {
            featureList.add(v);
        }
        List<String> queryOutputFields = Arrays.asList("image_path");

        SearchParam faceSearch = SearchParam.newBuilder()
                .withCollectionName(milvusConfiguration.collectionName)
                .withMetricType(MetricType.IP)
                .withVectorFieldName("image_feature")
                .withVectors(Collections.singletonList(featureList))
                .withOutFields(queryOutputFields)
                .withRoundDecimal(3)
                .withTopK(10).build();
        // 执行搜索
        long l = System.currentTimeMillis();
        R<SearchResults> respSearch = milvusServiceClient.search(faceSearch);
        log.info("MilvusServiceClient.search cost [{}]", System.currentTimeMillis() - l);
        // 解析结果数据
        SearchResultData results = respSearch.getData().getResults();
        int scoresCount = results.getScoresCount();
        SearchResultsWrapper wrapperSearch = new SearchResultsWrapper(results);
        List<MilvusRes> milvusResList = new ArrayList<>();
        for (int i = 0; i < scoresCount; i++) {
            float score = wrapperSearch.getIDScore(0).get(i).getScore();
            Object imagePath = wrapperSearch.getFieldData("image_path", 0).get(i);
            MilvusRes milvusRes = MilvusRes.builder().score(score).imagePath(imagePath.toString()).build();
            milvusResList.add(milvusRes);
        }
        return milvusResList;
    }

2.SeetaSDK工具类

SeetaSDK的Maven依赖:

        <dependency>
            <groupId>com.seeta</groupId>
            <artifactId>sdk</artifactId>
            <version>1.2.1</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/seeta-sdk-platform-1.2.1.jar</systemPath>
        </dependency>
       		<!--注意-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>       

jar是从官网下的源码进行的打包:

在这里插入图片描述
工具类主类:

@Slf4j
@Component
public class FaceUtil {

    static {
        // 加载本地方法
        LoadNativeCore.LOAD_NATIVE(SeetaDevice.SEETA_DEVICE_AUTO);
    }

    @Resource
    private SeetaModelConfiguration seetaModelConfiguration;

    private FaceDetectorProxy faceDetectorProxy;
    private FaceLandmarkerProxy faceLandmarkerProxy;
    private FaceRecognizerProxy faceRecognizerProxy;
    private AgePredictorProxy agePredictorProxy;
    private GenderPredictorProxy genderPredictorProxy;
    private MaskDetectorProxy maskDetectorProxy;
    private EyeStateDetectorProxy eyeStateDetectorProxy;

}

主类里的初始方法:

    @PostConstruct
    private void inti() {
        String basePath = seetaModelConfiguration.basePath;
        try {
            // 人脸识别检测器对象池配置
            SeetaConfSetting detectorPoolSetting = new SeetaConfSetting(
                    new SeetaModelSetting(0, new String[]{basePath + seetaModelConfiguration.faceDetectorModelFileName},
                            SeetaDevice.SEETA_DEVICE_AUTO));
            faceDetectorProxy = new FaceDetectorProxy(detectorPoolSetting);
            // 关键点定位器【默认使用5点可通过配置切换为68点】
            SeetaConfSetting faceLandmarkerPoolSetting = new SeetaConfSetting(
                    new SeetaModelSetting(1, new String[]{basePath + seetaModelConfiguration.faceLandmarkerModelFileName},
                            SeetaDevice.SEETA_DEVICE_AUTO));
            faceLandmarkerProxy = new FaceLandmarkerProxy(faceLandmarkerPoolSetting);
            // 人脸向量特征提取和对比器
            SeetaConfSetting faceRecognizerPoolSetting = new SeetaConfSetting(
                    new SeetaModelSetting(2, new String[]{basePath + seetaModelConfiguration.faceRecognizerModelFileName},
                            SeetaDevice.SEETA_DEVICE_AUTO));
            faceRecognizerProxy = new FaceRecognizerProxy(faceRecognizerPoolSetting);
            // 年龄评估器
            SeetaConfSetting agePredictorPoolSetting = new SeetaConfSetting(
                    new SeetaModelSetting(3, new String[]{basePath + seetaModelConfiguration.agePredictorModelFileName},
                            SeetaDevice.SEETA_DEVICE_AUTO));
            agePredictorProxy = new AgePredictorProxy(agePredictorPoolSetting);
            // 性别识别器
            SeetaConfSetting genderPredictorPoolSetting = new SeetaConfSetting(
                    new SeetaModelSetting(4, new String[]{basePath + seetaModelConfiguration.genderPredictorModelFileName},
                            SeetaDevice.SEETA_DEVICE_AUTO));
            genderPredictorProxy = new GenderPredictorProxy(genderPredictorPoolSetting);
            // 口罩检测器
            SeetaConfSetting maskDetectorPoolSetting = new SeetaConfSetting(
                    new SeetaModelSetting(5, new String[]{basePath + seetaModelConfiguration.maskDetectorModelFileName},
                            SeetaDevice.SEETA_DEVICE_AUTO));
            maskDetectorProxy = new MaskDetectorProxy(maskDetectorPoolSetting);
            // 眼睛状态检测
            SeetaConfSetting eyeStaterPoolSetting = new SeetaConfSetting(
                    new SeetaModelSetting(5, new String[]{basePath + seetaModelConfiguration.eyeStateModelFileName},
                            SeetaDevice.SEETA_DEVICE_AUTO));
            eyeStateDetectorProxy = new EyeStateDetectorProxy(eyeStaterPoolSetting);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

主类里的根据图片路径获取脸部特征向量方法:

    /**
     * 根据图片路径获取脸部特征向量
     *
     * @param imagePath 图片路径
     * @return 脸部特征向量
     */
    public float[] getFaceFeaturesByPath(String imagePath) {
        try {
            // 照片人脸识别
            SeetaImageData image = SeetafaceUtil.toSeetaImageData(imagePath);
            SeetaRect[] detects = faceDetectorProxy.detect(image);
            // 人脸关键点定位【主驾或副驾仅有一个人脸,多个人脸仅取第一个】
            if (detects.length > 0) {
                SeetaPointF[] pointFace = faceLandmarkerProxy.mark(image, detects[0]);
                // 人脸向量特征提取features
                return faceRecognizerProxy.extract(image, pointFace);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

主类里的根据人像图片的路径获取其属性【年龄、性别、是否戴口罩、眼睛状态】方法:

    /**
     * 根据人像图片的路径获取其属性【年龄、性别、是否戴口罩、眼睛状态】
     *
     * @param imagePath 图片路径
     * @return 图片属性 MAP 对象
     */
    public Map<String, Object> getAttributeByPath(String imagePath) {
        long l = System.currentTimeMillis();
        Map<String, Object> attributeMap = new HashMap<>(4);
        try {
            // 监测人脸
            SeetaImageData image = SeetafaceUtil.toSeetaImageData(imagePath);
            SeetaRect[] detects = faceDetectorProxy.detect(image);
            if (detects.length > 0) {
                SeetaPointF[] pointFace = faceLandmarkerProxy.mark(image, detects[0]);
                // 获取年龄
                int age = agePredictorProxy.predictAgeWithCrop(image, pointFace);
                attributeMap.put("age", age);
                // 性别
                GenderPredictor.GENDER gender = genderPredictorProxy.predictGenderWithCrop(image, pointFace).getGender();
                attributeMap.put("gender", gender);
                // 口罩
                boolean mask = maskDetectorProxy.detect(image, detects[0]).getMask();
                attributeMap.put("mask", mask);
                // 眼睛
                EyeStateDetector.EYE_STATE[] eyeStates = eyeStateDetectorProxy.detect(image, pointFace);
                attributeMap.put("eye", Arrays.toString(eyeStates));
                log.info("getAttributeByPath [{}] cost [{}]", imagePath, System.currentTimeMillis() - l);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return attributeMap;
        }
        return attributeMap;
    }
<think>我们被要求回答如何集成MySQL数据库Milvus向量库。根据引用内容,Milvus是一个专门用于向量存储检索的数据库,而MySQL是传统的关系型数据库。集成这两种数据库通常是为了结合结构化数据非结构化数据的处理能力。 常见的集成模式是:使用MySQL存储结构化数据(如文本、ID、时间戳等),而Milvus存储对应的向量数据(例如通过深度学习模型提取的特征向量)。然后通过一个共同的ID(例如主键)将两者关联起来。 步骤概述: 1. 设计数据模型:确定哪些数据存储在MySQL,哪些存储在Milvus,并设计关联键(如UUID)。 2. 数据写入流程:当有新的数据需要存储时,先提取向量(例如使用深度学习模型),然后将向量存入Milvus,同时将结构化数据(包括向量Milvus中的ID其他属性)存入MySQL。 3. 数据查询流程:当需要进行相似性搜索时,先通过Milvus搜索相似向量,得到相似向量的ID,然后通过这些ID到MySQL中查询对应的结构化信息。 具体步骤: 1. **设计数据表(MySQL)**: 在MySQL中,创建一张表用于存储结构化数据,同时包含一个字段用于存储对应向量Milvus中的ID(例如`milvus_id`)。例如: ```sql CREATE TABLE products ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255), description TEXT, milvus_id BIGINT, -- 用于存储Milvus返回的向量ID created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` 2. **数据插入**: - 首先,对需要向量化的数据(如图片、文本)进行特征提取,得到特征向量。 - 将特征向量插入Milvus,并获取Milvus返回的向量ID(一个整数或字符串)。 - 将结构化数据(如产品名称、描述)Milvus返回的向量ID一起插入到MySQL表中。 3. **相似性搜索**: - 当给定一个查询向量(例如用户上传的图片的特征向量)时,先在Milvus中进行相似性搜索,得到最相似的k个向量的ID(以及相似度分数)。 - 使用这些向量ID(即`milvus_id`)到MySQL中查询对应的结构化数据。 - 将查询结果(结构化数据)相似度分数返回给用户。 4. **更新删除**: - 更新:如果需要更新向量,则需要同时更新Milvus中的向量MySQL中的结构化数据。注意,更新向量时,Milvus中需要先删除旧向量再插入新向量(因为Milvus不支持直接更新向量),然后更新MySQL中对应的`milvus_id`(因为新插入的向量会有新的ID)。如果只是更新结构化数据,则只需更新MySQL。 - 删除:根据ID在MySQL中删除记录,同时根据`milvus_id`在Milvus中删除对应的向量。 5. **事务一致性**: 由于涉及两个数据库,需要保证数据的一致性。通常,我们可以通过应用层逻辑来实现: - 在插入时,先插入Milvus获取ID后再插入MySQL。如果MySQL插入失败,则回滚Milvus的插入(删除刚插入的向量)。 - 在删除时,先删除MySQL记录,再删除Milvus中的向量。如果删除Milvus失败,可以重试或记录错误。 - 也可以使用分布式事务(如XA事务)或者消息队列来保证最终一致性,但这会增加系统复杂度。 6. **代码示例(Python)**: 以下是一个简化的示例,展示如何将数据同时插入到MySQLMilvus,以及如何进行搜索。 ```python import pymysql from pymilvus import connections, FieldSchema, CollectionSchema, DataType, Collection, utility # 连接Milvus connections.connect("default", host="localhost", port="19530") # 定义Milvus集合(相当于表)的结构 fields = [ FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True), FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=128) # 假设向量维度为128 ] schema = CollectionSchema(fields, "example_collection") collection = Collection("example_collection", schema) # 创建索引(以IVF_FLAT为例) index_params = { "index_type": "IVF_FLAT", "metric_type": "L2", "params": {"nlist": 128} } collection.create_index("embedding", index_params) # 连接MySQL mysql_conn = pymysql.connect(host='localhost', user='root', password='password', database='test') # 插入数据示例 def insert_data(name, description, embedding): # 插入Milvus mr = collection.insert([[embedding]]) # 注意:embedding是list of list,因为可以插入多条 # 获取自动生成的向量ID(这里插入一条,所以取第一个) milvus_id = mr.primary_keys[0] # 插入MySQL cursor = mysql_conn.cursor() sql = "INSERT INTO products (name, description, milvus_id) VALUES (%s, %s, %s)" cursor.execute(sql, (name, description, milvus_id)) mysql_conn.commit() cursor.close() # 搜索示例 def search_similar_vectors(query_embedding, top_k=10): # 在Milvus中搜索 collection.load() search_params = {"metric_type": "L2", "params": {"nprobe": 16}} results = collection.search([query_embedding], "embedding", search_params, top_k) # 获取结果中的向量ID列表 milvus_ids = [result.id for result in results[0]] # 注意:results[0]表示第一个查询的结果 # 根据milvus_ids在MySQL中查询 cursor = mysql_conn.cursor() # 注意:milvus_ids是整数列表,需要转换为逗号分隔的字符串 ids_str = ','.join(str(id) for id in milvus_ids) sql = f"SELECT * FROM products WHERE milvus_id IN ({ids_str}) ORDER BY FIELD(milvus_id, {ids_str})" cursor.execute(sql) rows = cursor.fetchall() cursor.close() return rows # 注意:实际使用中,需要处理异常关闭连接等 ``` 7. **注意事项**: - **性能**:由于涉及两个数据库的交互,需要确保网络延迟查询效率。批量操作可以减少网络开销。 - **错误处理**:在应用层需要仔细处理错误,以保证两个数据库的数据一致性。 - **ID映射**:Milvus支持自增ID(如上例)或用户自定义ID。如果使用自增ID,则MySQL中存储的`milvus_id`就是Milvus生成的ID。如果使用自定义ID,则需要在插入Milvus时指定,并且确保这个ID在MySQL中也是可用的(例如使用MySQL中的主键作为Milvus的ID)。 - **版本兼容性**:注意Milvus的版本(如1.x2.x)API有所不同,上述示例基于Milvus2.x。 8. **其他集成方式**: - 使用Milvus的Attu管理工具:Attu是Milvus的图形化管理工具,但不直接支持MySQL集成。 - 使用ORM框架:可以结合SQLAlchemy等ORM框架来简化MySQL操作。 - 使用中间件:开发一个中间服务层,统一处理数据的分发查询。 通过以上步骤,就可以实现MySQLMilvus的集成。这种集成方式充分利用了两种数据库的优势:Milvus的高效向量检索MySQL的事务性、结构化查询能力。 根据引用[^1][^2][^3]的内容,Milvus是一个强大的向量数据库,适合处理向量相似性搜索,而MySQL则负责存储结构化数据。两者结合可以构建更强大的应用。 §§相关问题§§ 1. 在集成MySQLMilvus时,如何保证数据的一致性? 2. Milvus支持哪些索引类型,它们分别适用于什么场景? 3. 除了MySQL,Milvus还可以与哪些数据库进行集成? 4. 在向量搜索中,如何处理高维向量的存储查询效率问题?</think>### MySQL与Milvus数据库集成指南 要将MySQL(关系型数据库)与Milvus向量数据库)集成,核心思路是通过**共享标识符关联结构化数据与向量数据**。以下是具体实现方案: --- #### **集成架构设计** ```mermaid graph LR A[应用程序] --> B[MySQL] A --> C[Milvus] B -->|存储结构化数据| D[ID/元数据/业务字段] C -->|存储向量数据| E[向量+关联ID] D & E -->|通过共享ID关联| F[联合查询结果] ``` --- #### **具体实现步骤** 1. **数据模型设计** - **MySQL表结构**(示例): ```sql CREATE TABLE products ( id BIGINT PRIMARY KEY, -- 与Milvus共享的ID name VARCHAR(255), price DECIMAL(10,2), description TEXT, created_at TIMESTAMP ); ``` - **Milvus集合结构**(示例): ```python from pymilvus import FieldSchema, CollectionSchema fields = [ FieldSchema(name="id", dtype=DataType.INT64, is_primary=True), FieldSchema(name="feature_vector", dtype=DataType.FLOAT_VECTOR, dim=512) # 512维向量 ] schema = CollectionSchema(fields, description="Product Vectors") ``` 2. **数据写入流程** ```python # 同时写入MySQLMilvus def insert_data(product_data, feature_vector): # 1. 写入MySQL mysql_cursor.execute(""" INSERT INTO products (id, name, price) VALUES (%s, %s, %s) """, (product_data["id"], product_data["name"], product_data["price"])) # 2. 写入Milvus milvus_collection.insert([ [product_data["id"]], # ID字段 [feature_vector] # 向量数据 ]) ``` 3. **联合查询示例(相似商品搜索)** ```python def search_similar_products(query_vector, top_k=10): # 1. 在Milvus中搜索相似向量 milvus_results = milvus_collection.search( data=[query_vector], anns_field="feature_vector", param={"metric_type": "L2", "params": {"nprobe": 16}}, limit=top_k ) # 2. 提取关联ID product_ids = [result.id for result in milvus_results[0]] # 3. 从MySQL获取详细信息 mysql_cursor.execute(f""" SELECT * FROM products WHERE id IN ({','.join(map(str, product_ids))}) """) return mysql_cursor.fetchall() ``` --- #### **关键技术点** 1. **ID映射机制** - 使用相同的主键(如UUID或自增ID)作为跨库关联依据 - MySQL的`id`字段 ↔ Milvus的`primary key`字段 2. **同步策略** - **实时双写**:事务中同时写入两库(需处理失败回滚) - **异步队列**:通过Kafka/RabbitMQ解耦,确保最终一致性 - **批处理**:定期同步增量数据(适用离线场景) 3. **连接配置** - **MySQL连接**:标准JDBC/PyMySQL驱动 - **Milvus连接**: ```python from pymilvus import connections connections.connect(alias="default", host='127.0.0.1', port='19530') ``` --- #### **典型应用场景** 1. **电商推荐系统** - MySQL:存储商品价格、描述等属性 - Milvus:存储商品图像/文本特征向量,实现相似商品搜索[^3] 2. **跨模态检索** - MySQL:存储文本元数据 - Milvus:存储图文/音视频的Embedding向量 3. **智能客服** - MySQL:存储用户订单历史 - Milvus:存储问题语义向量,匹配相似问答对 --- #### **注意事项** 1. **事务一致性** 分布式事务建议采用Saga模式或TCC补偿机制[^1] 2. **性能优化** - 对高频查询的向量字段建立`IVF_FLAT`或`HNSW`索引[^3] - MySQL中为关联ID字段建立索引 3. **资源隔离** Milvus数据节点与MySQL服务器分开部署,避免资源竞争 4. **版本兼容性** 推荐使用Milvus 2.x(改进的分布式架构)[^1] --- #### **示例工具链** | 组件 | 推荐工具 | |---------------|----------------------------------| | 向量提取 | PyTorch/TensorFlow Transformers | | 数据管道 | Apache Airflow/Luigi | | 服务部署 | Docker + Kubernetes | | 监控 | Prometheus + Grafana | > 完整代码示例参考:[Milvus官方示例库](https://github.com/milvus-io/bootcamp)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yuanzhengme

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值