【Milvus的以文搜图】

news2025/1/14 18:14:18

0. 介绍

以文搜图指的是,根据文本描述,从图像数据库中检索与文本内容相似的图像数据并返回。通过在CSDN中搜索以文搜图,找到了如下两篇文章:

  • 从零到一,教你搭建「以文搜图」搜索服务(一)_Zilliz Planet的博客-CSDN博客_以文搜图 搜索引擎技术
  • 从零到一,教你搭建「CLIP 以文搜图」搜索服务(二):5 分钟实现原型_Zilliz Planet的博客-CSDN博客

 这两篇文章的内容介绍了以文搜图的原理,以及如何使用Faiss来搭建以文搜图的应用。

与上述两篇文章不同,本文主要讲述如何基于Chinese-CLIP和Milvus来搭建一个以文搜图应用。

1. Chinese-CLIP

CLIP是OpenAI在2021年提出的多模态神经网络模型,该模型基于OpenAI 收集到的 4 亿对图像文本对进行训练,分别将文本和图像进行编码,之后使用 metric learning 进行权重,其目标是将图像与文本的相似性提高,大致如下图所示。具体内容不在本文赘述。

 Chinese-CLIP是CLIP模型的中文版本,使用大规模的图文对进行训练得到,针对中文领域数据能够实现更好的效果。本文的以文搜图应用使用中文CLIP作为文本和图像的特征提取器,提取数据集中所有图像的特征作为数据库,用于检索。

1.1 安装中文CLIP

中文CLIP的README文件详细描述了如何安装、跨模态检索的Finetune、预测和评估,以及零样本分类等内容,本文主要基于该文件,进行相关实验。

依赖环境

安装中文CLIP库之前,需要满足如下环境配置:

  • python >= 3.6.4
  • pytorch >= 1.8.0 (with torchvision >= 0.9.0)
  • CUDA Version >= 10.2

pip安装

# 通过pip安装
pip install cn_clip

 功能测试

通过下面的代码,可以验证中文CLIP库是否安装正确,主要内容是提取给定的图像和文本特征,并计算它们之间的相似度。

import torch
from PIL import Image

import cn_clip.clip as clip
from cn_clip.clip import load_from_name, available_models
print("Available models:", available_models())
# Available models: ['ViT-B-16', 'ViT-L-14', 'ViT-L-14-336', 'ViT-H-14', 'RN50']

device = "cpu"
if torch.cuda.is_available():
    device = "cuda"

model, preprocess = load_from_name("RN50", \
                                    device=device,
                                    download_root="./")

model.eval()

# image preprocess

image_data = Image.open("examples/pokemon.jpeg")
infer_data = preprocess(image_data).unsqueeze(0).to(device)

# text data
text_data = clip.tokenize(["杰尼龟", "妙蛙种子", "小火龙", "皮卡丘"]).to(device)

with torch.no_grad():
    # image_features = model.encode_image(infer_data)
    # text_features  = model.encode_text(text_data)

    # # 对特征进行归一化,请使用归一化后的图文特征用于下游任务
    # image_features /= image_features.norm(dim=-1, keepdim=True) 
    # text_features /= text_features.norm(dim=-1, keepdim=True)

    logits_per_image, logits_per_text = model.get_similarity(infer_data, text_data)
    probs = logits_per_image.softmax(dim=-1).cpu().numpy()

print("Label probs:", probs)  

 现阶段,中文CLIP提供5种不同规模的模型,如下图所示,上述例子使用了RN50,probs输出pookemon图像与4个文本之间的相似性。

 1.2 提取图文特征

在README文件中,跨模态检索部分介绍了如何在下游数据集中对模型进行finetune、预测和评估模型性能。

本节中,主要内容是基于中文clip如何提取图文特征,即使用现有的预训练模型,不做finetune,对数据集进行推理,得到数据集图文特征文件。

准备数据集

按照介绍,为了提高效率,需要将数据集转换为tsv和jsonl格式的文件,然后将其转换为内存索引的LMDB数据库文件。为了简化实验的步骤,本文直接使用项目中提供的已经转换好的数据集,进行后续的实验,如下图所示,下载文件放到对应文件目录下。本文以Flickr30K-CN数据集进行实验。

 文件结构如下所示:

 图文特征提取

通过执行如下命令,能够完成对数据集中图像和文本的特征提取。

#!/bin/bash

# Usage: extract image and text features
# only supports single-GPU inference
export CUDA_VISIBLE_DEVICES=${1}
export PYTHONPATH=${PYTHONPATH}:`pwd`/cn_clip

SPLIT=${2}
DATAPATH=${3}
RESUME=${DATAPATH}/pretrained_weights/clip_cn_rn50.pt
DATASET_NAME=Flickr30k-CN

python -u cn_clip/eval/extract_features.py \
    --extract-image-feats \
    --extract-text-feats \
    --image-data="${DATAPATH}/datasets/${DATASET_NAME}/lmdb/${SPLIT}/imgs" \
    --text-data="${DATAPATH}/datasets/${DATASET_NAME}/${SPLIT}_texts.jsonl" \
    --img-batch-size=32 \
    --text-batch-size=32 \
    --context-length=52 \
    --resume=${RESUME} \
    --vision-model=RN50 \
    --text-model=RBT3-chinese

其中第一个参数是用于推理的GPU ID,第二参数DATAPATH指的数据存放的目录,split指的是train、test或者valid。图片特征保存于${split}_imgs.img_feat.jsonl文件,文本特征则保存于${split}_texts.txt_feat.jsonl

KNN检索

在中文CLIP项目中,提供了一个简单的KNN检索,用来计算文到图、图到文检索的top-k召回结果,本文主要计算从文到图的检索结果,与后续使用Milvus进行检索的结果对比。

#!/bin/bash

# Usage: topK search
# only supports single-GPU inference
export CUDA_VISIBLE_DEVICES=${1}
export PYTHONPATH=${PYTHONPATH}:`pwd`/cn_clip

SPLIT=${2}
DATAPATH=${3}
RESUME=${DATAPATH}/pretrained_weights/clip_cn_rn50.pt
DATASET_NAME=Flickr30k-CN

python -u cn_clip/eval/make_topk_predictions.py \
    --image-feats="${DATAPATH}/datasets/${DATASET_NAME}/${SPLIT}_imgs.img_feat.jsonl" \
    --text-feats="${DATAPATH}/datasets/${DATASET_NAME}/${SPLIT}_texts.txt_feat.jsonl" \
    --top-k=10 \
    --eval-batch-size=32768 \
    --output="${DATAPATH}/datasets/${DATASET_NAME}/${SPLIT}_predictions.jsonl"

结果保存到${split}_predictions.jsonl文件中,每行表示一个文本召回的top-k图片id,格式如下:

{"text_id": 153915, "image_ids": [5791244, 1009692167, 7454547004, 3564007203, 38130571, 2525270674, 2195419145, 2503091968, 4966265765, 3690431163]}

 至此,对于中文CLIP如何提取图像和文本特征有了大致的了解,接下来,将中文CLIP提取的图像特征存储到Milvus中,然后输入文本,CLIP提取文本特征,在图像数据库中检索得到topK个内容与文本相似的图像结果。

2. Milvus

如前面文章介绍,Milvus是一个向量数据库,基于深度学习网络提取的特征进行对象之间相似度计算,返回topK个相似的结果。

不同的检索任务,需要对milvus和mysql的数据内容进行设计,以及如何提取对象的特征是十分重要的。

 以文搜图的整体流程如上图所示,图片来自链接:

1. 构建图像特征库

图像数据集通过多模态模型提取特征,本文中使用中文CLIP多模态模型,然后将特征存储到Milvus中,用于后续的检索。

2. 文本检索

请求文本通过多模态模型提取特征,得到文本特征,然后从Milvus数据库中进行检索,得到topK个相似的内容。

2.1 构建特征提取网络

 如下方的代码所示,基于中文CLIP构建一个特征提取类,使用extract_image_featuresh和extract_text_features提取图像和文本特征。

build_Flickr30kCN_image_db函数用于从第一部分生成的图像特征文件中提取图像ID和特征来构建图像数据库,用于后续的检索任务。

load_images函数用于提取图像ID和以base64编码的二进制图像数据,后续插入到MySQL中,检索时,返回相应的图像内容。

import torch
from PIL import Image
import os
import json
from tqdm import tqdm
import cn_clip.clip as clip
from cn_clip.clip import available_models
print("Available models:", available_models())
# Available models: ['ViT-B-16', 'ViT-L-14', 'ViT-L-14-336', 'ViT-H-14', 'RN50']



class Chinese_CLIP:
    def __init__(self) -> None:
        self.device = "cpu"
        if torch.cuda.is_available():
            self.device = "cuda"
        self.splits = ["valid"]
        self.model, self.preprocess = clip.load_from_name(  "RN50", \
                                                            device=self.device,
                                                            download_root="./")
        self.model.eval()

    def extract_image_features(self, img_name):        # for upload
        image_data = Image.open(img_name).convert("RGB")
        infer_data = self.preprocess(image_data)
        infer_data = infer_data.unsqueeze(0).to(self.device)
        with torch.no_grad():
            image_features = self.model.encode_image(infer_data)
        image_features /= image_features.norm(dim=-1, keepdim=True) 

        return image_features.cpu().numpy()[0]     # [1, 1024]

    def extract_text_features(self, text):            # for search

        text_data = clip.tokenize([text]).to(self.device)
        with torch.no_grad():
            text_features = self.model.encode_text(text_data)
        text_features /= text_features.norm(dim=-1, keepdim=True)
        return text_features.cpu().numpy()[0]      # [1, 1024]
    
    def build_Flickr30kCN_image_db(self, data_path):        # for load 
        image_ids = list()
        image_feats = list()
        for split in self.splits:
            image_path = os.path.join(data_path, split + "_imgs.img_feat.jsonl")
            if not os.path.isfile(image_path):
                print("error, file {} is not exist.".format(image_path))
                continue
            with open(image_path, "r") as fin:
                for line in tqdm(fin, desc="build image {} part: ".format(split)):
                    obj = json.loads(line.strip())
                    image_ids.append(obj['image_id'])
                    image_feats.append(obj['feature'])
        return image_ids, image_feats
    
    
    def load_images(self, data_path):        # for mysql
        image_dicts = dict()
        for split in self.splits:
            file_path = os.path.join(data_path, split + "_imgs.tsv")
            if not os.path.isfile(file_path):
                print("error, file {} is not exist.".format(file_path))
            with open(file_path, "r") as fin_imgs:
                for line in tqdm(fin_imgs):
                    line = line.strip()
                    image_id, b64 = line.split("\t")
                    image_dicts[image_id] = b64.encode()
        return image_dicts               

2.2 设计collection

和其他任务一样,以文搜图任务的collection内容设计如下,milvus index是milvus生成的索引index,embedding是图像的特征。

  def create_collection(self, collection_name):       # 创建collection对象
        # Create milvus collection if not exists
        try:
            if not self.has_collection(collection_name):
                field1 = FieldSchema(name="id", dtype=DataType.INT64, descrition="int64", is_primary=True, auto_id=True)
                field2 = FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, descrition="float vector",
                                     dim=VECTOR_DIMENSION, is_primary=False)
                schema = CollectionSchema(fields=[field1, field2], description="collection description")
                self.collection = Collection(name=collection_name, schema=schema)
                LOGGER.debug(f"Create Milvus collection: {collection_name}")
            else:
                # utility.drop_collection(collection_name)
                # self.create_collection(collection_name)
                self.set_collection(collection_name)
            return "OK"
        except Exception as e:
            LOGGER.error(f"Failed to create collection to Milvus: {e}")
            sys.exit(1)

2.3 设计MySQL表

与Milvus对应的,MySQL table的设计如下,milvus index是milvus生成的索引index,image id是图像对应的图像名,image data是base64编码后的二进制图像数据。

    def create_mysql_table(self, table_name):
        # Create mysql table if not exists
        self.test_connection()
        sql = "create table if not exists " + table_name + "(milvus_id TEXT, image_id TEXT, image_data MEDIUMBLOB not null );"
        try:
            self.cursor.execute(sql)
            LOGGER.debug(f"MYSQL create table: {table_name} with sql: {sql}")
        except Exception as e:
            LOGGER.error(f"MYSQL ERROR: {e} with sql: {sql}")
            sys.exit(1)
    def load_data_to_mysql(self, table_name, data):
        # Batch insert (Milvus_ids, img_path) to mysql
        self.test_connection()
        sql = "insert into " + table_name + " (milvus_id,image_id, image_data) values (%s,%s, %s);"
        try:
            self.cursor.executemany(sql, data)
            self.conn.commit()
            LOGGER.debug(f"MYSQL loads data to table: {table_name} successfully")
        except Exception as e:
            LOGGER.error(f"MYSQL ERROR: {e} with sql: {sql}")
            sys.exit(1)

2.4 构建图像数据库

在operations目录下的load文件的功能是构建图像特征库,本文是从第一部分的文件中解析得到图像ID和图像特征,并将其插入到milvus中,将图像id和图像数据插入MySQL中。

# Get the vector of images
def extract_features(data_path, model):
    try:
        image_ids, image_feats = model.build_Flickr30kCN_image_db(data_path)
        print(f"Extracting feature from {len(image_ids)} images in total")
            
        return image_ids, image_feats
    except Exception as e:
        LOGGER.error(f"Error with extracting feature from image {e}")
        sys.exit(1)


# Combine the id of the vector and the name of the image into a list
def format_data(ids, image_ids, image_dicts):
    data = []
    for i in range(len(ids)):
        value = (str(ids[i]), str(image_ids[i]), image_dicts[str(image_ids[i])])
        data.append(value)
    return data


# Import vectors to Milvus and data to Mysql respectively
def do_load(table_name, image_dir, model, milvus_client, mysql_cli):
    if not table_name:
        table_name = DEFAULT_TABLE
    # 利用模型提取图像特征得到特征向量
    image_ids, vectors = extract_features(image_dir, model)

    ids = milvus_client.insert(table_name, vectors)
    milvus_client.create_index(table_name)

    image_dicts = model.load_images(image_dir)

    mysql_cli.create_mysql_table(table_name)
    mysql_cli.load_data_to_mysql(table_name, format_data(ids, image_ids, image_dicts))
    return len(ids)

2.5 文本检索

在operations目录下的search文件功能是根据给定的文本内容,执行图像检索,具体实现如下:

def do_search(table_name, text_content, top_k, model, milvus_client, mysql_cli):
    try:
        if not table_name:
            table_name = DEFAULT_TABLE
        features = model.extract_text_features(text_content)
        vectors = milvus_client.search_vectors(table_name, [features], top_k)
        vids = [str(x.id) for x in vectors[0]]
        paths = mysql_cli.search_by_milvus_ids(vids, table_name)
        distances = [x.distance for x in vectors[0]]
        return paths, distances
    except Exception as e:
        LOGGER.error(f"Error with search : {e}")
        sys.exit(1)

2.6  配置文件

同样,需要修改config文件中collection名称和特征的维度。

3. FastApi服务端

在main文件中,由于任务发生了变化,因此需要对search条目的函数进行修改,输入文本内容,将输出的base64编码的图像数据解码保存下来。

@app.post('/img/search')
async def search_images( table_name: str = None, content: str = None, topk: int = Form(TOP_K)):
    # Search the upload image in Milvus/MySQL
    try:
        shutil.rmtree(UPLOAD_PATH)
        os.mkdir(UPLOAD_PATH)
        paths, distances = do_search(table_name, content, topk, MODEL, MILVUS_CLI, MYSQL_CLI)
        res = list()
        for data, dist in zip(paths, distances):
            image_path = os.path.join(UPLOAD_PATH, data["image_id"] + ".jpg")
            with open(image_path, "wb") as f:
                img_data = base64.b64decode(data["image_data"])
                f.write(img_data)

            res.append({
                "distance": dist,
                "image_id": image_path,
                # "image_data": data["image_data"]
            })
        # res = sorted(res.items(), key=lambda item: item[1])
        res.sort(key=lambda item: item["distance"])
        LOGGER.info("Successfully searched similar images!")
        return res
    except Exception as e:
        LOGGER.error(e)
        return {'status': False, 'msg': e}, 400

 同样,使用uvicorn main:app --reload命令启动fastapi服务,在浏览器中进入docs页面。

3.1 构建图像数据库

进入/img/load条目,输入collection名称和图像特征文件存放的目录,执行。

 后台返回日志如下:

 数据文件目录结果如下:

3.2 执行文本检索

本文以Flickr30k-CN数据集中valid子集作为实验,其中的一些文本描述如下:

{"text_id": 148915, "text": "这个小女孩穿着古怪的有条纹的裤子是图,你必须要问,这是更大的,她的脚和她的疲倦的微笑。", "image_ids": [104816788]}
{"text_id": 148916, "text": "一个熟睡的婴儿是在某人的手臂上,穿着一件粉红色条纹的衣服。", "image_ids": [104816788]}
{"text_id": 148917, "text": "一个熟睡的婴儿在一个粉红色的条纹衣服。", "image_ids": [104816788]}
{"text_id": 148918, "text": "在某人的怀里抱着一个小婴儿。", "image_ids": [104816788]}
{"text_id": 148919, "text": "一个小宝宝穿着紫色的裤子。", "image_ids": [104816788]}
{"text_id": 148920, "text": "一个穿着蓝色游泳裤的男孩在水里滑了一个黄色的幻灯片,在水里漂浮着充气玩具。", "image_ids": [1077546505]}
{"text_id": 148921, "text": "一个孩子正从一个漂浮在水面上的彩色气球上掉下来。", "image_ids": [1077546505]}
{"text_id": 148922, "text": "一个男孩在一个池塘里滑了一个彩色的管子。", "image_ids": [1077546505]}
{"text_id": 148923, "text": "一个穿着蓝色短裤的男孩在一个游泳池里滑了一个幻灯片。", "image_ids": [1077546505]}
{"text_id": 148924, "text": "一个男孩骑到一个小后院游泳池。", "image_ids": [1077546505]}
{"text_id": 148925, "text": "两家人正在厨房准备食物。", "image_ids": [1079013716]}
{"text_id": 148926, "text": "厨师为顾客准备汉堡。", "image_ids": [1079013716]}
{"text_id": 148927, "text": "两家厨师在餐厅厨房准备汉堡。", "image_ids": [1079013716]}
{"text_id": 148928, "text": "两家人在厨房准备食物。", "image_ids": [1079013716]}
{"text_id": 148929, "text": "厨房烹饪中的2个厨师。", "image_ids": [1079013716]}

 在search条目中,使用如上的一个描述进行检索

 返回的topK结果如下:

3.3 结果对比

通过对比milvus返回的结果和中文CLIP项目中KNN检索的结果,来判断检索的正确性。找到上述文本对应的text ID,在valid_predictions.jsonl文件中,找到对应的topK个Image ID。

KNN 检索结果:

{"text_id": 148924, "image_ids": [989754491, 2508918369, 1077546505, 154094533, 4637950362, 1131340021, 3621652774, 1100214449, 811663364, 2687539673]}

Milvus返回的结果: 

[
  {
    "distance": 0.8329496383666992,
    "image_id": "tmp/search-images/989754491.jpg"
  },
  {
    "distance": 0.8518763780593872,
    "image_id": "tmp/search-images/2508918369.jpg"
  },
  {
    "distance": 0.8582218885421753,
    "image_id": "tmp/search-images/1077546505.jpg"
  },
  {
    "distance": 0.8794834613800049,
    "image_id": "tmp/search-images/154094533.jpg"
  },
  {
    "distance": 0.8805474638938904,
    "image_id": "tmp/search-images/4637950362.jpg"
  },
  {
    "distance": 0.8876401782035828,
    "image_id": "tmp/search-images/1131340021.jpg"
  },
  {
    "distance": 0.8878381848335266,
    "image_id": "tmp/search-images/3621652774.jpg"
  },
  {
    "distance": 0.8895268440246582,
    "image_id": "tmp/search-images/1100214449.jpg"
  },
  {
    "distance": 0.8895866870880127,
    "image_id": "tmp/search-images/811663364.jpg"
  },
  {
    "distance": 0.9175957441329956,
    "image_id": "tmp/search-images/2687539673.jpg"
  }
]

 通过对比,可以发现两者之间的结果一致。

4. 总结

本文,基于中文CLIP和Milvus简单实现以文搜图应用,使用Flickr30k-CN数据集中valid子集作为实验,FastAPI构建可交互的接口,完成小规模的文本检索图像功能。

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

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

相关文章

Linux “挂载” 的概念

0、前言 截至到写这个稿子,始终对挂载的概念有点模糊,到底是硬盘挂载到目录?还是目录挂载到硬盘呢?今天终于从《鸟哥的Linux私房菜》中推断出了答案,而且也恍然大悟地理解了之前书中一句晦涩难懂的话。 1、挂载的概念…

基于Web的Markdown编辑器HedgeDoc

什么是 HedgeDoc ? HedgeDoc 是一个开源的、基于 web 的、自托管的、协作的markdown编辑器。您可以使用它轻松地在笔记、图形甚至演示文稿上进行实时协作。用户需要做的就是将你的笔记链接分享给同事,他们就可以开始使用了。 不想自己搭建可以试试官方的…

基于有偏距离权值双线性插值原理(Weighted bilinear with warping)的图像超分辨重构研究-附Matlab程序

⭕⭕ 目 录 ⭕⭕✳️ 一、图像超分辨率重构原理✳️ 二、双线性插值重构理论与实验分析✳️ 2.1 双线性插值理论与实验验证✳️ 2.2 有偏距离双线性插值重构理论与实验验证✳️ 2.3 权重双线性插值理论与实验验证✳️ 2.4 有偏距离权值双线性插值理论与实验验证✳️ 三、参考文…

frps内网穿透

1 原理讲解 frp工作原理 服务端运行,监听一个主端口,等待客户端的连接; 客户端连接到服务端的主端口,同时告诉服务端要监听的端口和转发类型;服务端fork新的进程监听客户端指定的端口; 外网用户连接到客户…

2012-2020中国地区银行多指标数据

1、数据来源:bankscope 2、时间跨度:2012-2020-3季度 3、区域范围:中国 4、指标说明: 包含以下数据: 资产负债表,利润表,流动性表,资本充足率表,财务比率 global s…

java 高级面试题(借鉴)

谈谈ConcurrentHashMap的扩容机制 1.7版本 1. 1.7版本的ConcurrentHashMap是基于Segment分段实现的 2. 每个Segment相对于⼀个⼩型的HashMap 3. 每个Segment内部会进⾏扩容,和HashMap的扩容逻辑类似 4. 先⽣成新的数组,然后转移元素到新数组中 5. 扩容的…

vue3 antd项目实战——table表格(一文带你快速实现后台管理系统最常用的table表格)

零基础filter实现最简单的table表格知识调用核心干货下期预告关键字模糊查找(纯前端)关键字模糊查找(前后交互)知识调用 功能实现可能要用到的知识:vue3ant design vuets实战【ant-design-vue组件库引入】vue3项目实战…

超纯水如何除硼,除硼树脂技术分析

硼在超纯水中对晶圆厂的产品良品率的影响,那超纯水深度除硼的方式有哪些呢,在现今新型的微电子、太阳能等行业中,对超纯水的要求越来越高,对超纯水设备中PPb的硼和硅要求达到PPb级。但硼和硅属于弱电离元素,在水中不易…

泛型类的认识 - (了解数据结构的基础)

文章目录前言1. 为什么使用泛型类?2. 泛型类介绍总结前言 本篇通过介绍为什么使用泛型类,什么是泛型类,进一步为以后数据结构的学习打下基础。如有错误,请在评论区指正,让我们一起交流,共同进步&#xff0…

【强化学习论文合集】IJCAI-2021 强化学习论文

强化学习(Reinforcement Learning, RL),又称再励学习、评价学习或增强学习,是机器学习的范式和方法论之一,用于描述和解决智能体(agent)在与环境的交互过程中通过学习策略以达成回报最大化或实现特定目标的问题。 本专栏整理了近几年国际顶级会议中,涉及强化学习(Rein…

户外运动耳机推荐、这几款性能超强的户外运动耳机不可错过

在户外跑步的时候,也有不少朋友会选择戴上耳机,用音乐来”调味“,让跑步的过程不那么枯燥乏味。凡事有利就有弊,跑步时听音乐也如此,它的弊端之一是可能会有安全隐患。如果跑步时耳机音量开得太大,可能会忽…

JAVA-GUI工具的编写-----简易框架篇

好久没写东西了,毕竟一个屁民没那么多东西写的,来来回回就老三样,扯犊子的也不想写,今天给大家来个都感兴趣的-------如何编写自己的GUI工具? 当然了,IDEA怎么去破解,这里就不多比比&#xff0c…

java基础一:基础概念、面向对象

目录 1.基础概念 2.IDEA 开发工具 2.1 JDK环境配置 2.2 注释和变量 2.3 标识符 2.4 数据类型 2.5 算术运算符 2.6 赋值运算符 2.7 关系运算符 2.8 逻辑运算符 2.9 三元运算符 2.10 流程控制 3. 面向对象 3.1类和对象 3.2 静态 static 3.3 package 包 3.4 impor…

Spark系列之Spark概述

title: Spark系列 What is Apache Spark™? Apache Spark™ is a multi-language engine for executing data engineering, data science, and machine learning on single-node machines or clusters. 第一章 Spark概述 1.1 Spark的产生背景 1.1.1 MapReduce的发展 1.1.…

【Flink】时间语义和水位线的概念和使用

文章目录一 时间语义与Wartermark1 Flink中的时间语义2 EventTime的引入3 Watermark(水位线)(1)基本概念(2)水位线测试a 代码编写b 计算水位线c 计算结果d 深入分析(3)水位线时间测试…

【web前端期末大作业】html网上在线书城大学生静态网页 大学生html当当书城仿站 网上书城购物网页作业HTML

🎉精彩专栏推荐 💭文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (10…

智慧城市解决方案典型应用

4.2.智慧城市建设目标 4.2.1.高标准的智慧城市基础设施 智慧城市的基础设施主要包括城市信息基础设施和城市空间数据基础设施两个方面。智慧城市建设的首要目标是要建立起完善的、高标准的智慧城市基础设施,并在此基础上建立完备的城市基础信息资源。高标准的城市…

微软文本转语音「免费网页版」

网站地址:Text To Speech - 在线文本转语音 大家好~今天给小伙伴们安利一个AI配音小工具:TTS-文本转语音 【闲话】 疫情三年,很多人都失去工作,有的也是断断续续。很多人负债累累,在全球形势严峻,经济下滑…

【FreeRTOS(三)】任务状态

文章目录任务状态任务挂起 vTaskSuspend取消任务挂起 vTaskResume挂起任务调度器 vTaskSuspendAll取消挂起任务调度器 xTaskResumeAll代码示例:任务挂起、取消任务挂起代码示例:挂起任务调度器、取消挂起任务调度器任务状态 freeRTOS任务的状态有四种&am…

【POJ No. 3321】 子树查询 Apple Tree

【POJ No. 3321】 子树查询 Apple Tree 北大OJ 题目地址 【题意】 在卡卡的房子外面有一棵苹果树,树上有N 个叉(编号为1~N ,根为1),它们通过分支连接。苹果在叉上生长,两个苹果不会在同一个叉…