当前位置: 首页 > news >正文

【完整源码+数据集+部署教程】Aura棕榈油果实分割系统: yolov8-seg-C2f-DCNV2-Dynamic

背景意义

研究背景与意义

随着全球人口的持续增长和经济的发展,植物油的需求量日益增加,尤其是棕榈油,因其广泛应用于食品、化妆品及生物燃料等领域,成为了全球最重要的植物油之一。然而,棕榈油的生产往往伴随着环境问题,如森林砍伐、生物多样性丧失以及温室气体排放等。因此,如何提高棕榈油的生产效率,同时降低对环境的影响,成为了亟待解决的课题。在这一背景下,基于计算机视觉的农业智能化技术逐渐崭露头角,尤其是深度学习技术在图像识别和分割领域的应用,为棕榈油的精准农业管理提供了新的可能性。

本研究旨在开发一种基于改进YOLOv8的Aura棕榈油果实分割系统,利用先进的实例分割技术,对棕榈树上的不同成熟阶段的果实进行精确识别与分割。该系统的构建基于一个包含2300张图像的数据集,涵盖了四个主要类别:FROND(叶片)、RIPE(成熟果实)、STOICK(果实茎部)和UNRIPE(未成熟果实)。通过对这些类别的细致划分,研究不仅能够实现对棕榈果实的高效检测,还能为果实的成熟度分析提供数据支持,进而优化采摘时机,提高采摘效率。

改进YOLOv8模型的选择,源于其在实时目标检测和分割任务中的优越性能。YOLO系列模型以其高效的检测速度和较高的准确率,广泛应用于各种视觉任务。通过对YOLOv8进行改进,结合特定的棕榈果实特征,研究将进一步提升模型在复杂环境下的鲁棒性和适应性。这一改进不仅能提高分割精度,还能有效减少误检和漏检现象,为农业生产提供更为可靠的技术支持。

此外,Aura棕榈油果实分割系统的研究具有重要的现实意义。首先,它能够为棕榈油生产企业提供精准的果实监测和管理工具,帮助企业实现智能化管理,降低人工成本,提高生产效率。其次,该系统的应用将有助于推动可持续农业的发展,通过精确的果实成熟度分析,合理安排采摘时间,减少资源浪费,降低对环境的负面影响。最后,本研究的成果将为其他农作物的智能检测与管理提供借鉴,推动农业领域的数字化转型。

综上所述,基于改进YOLOv8的Aura棕榈油果实分割系统的研究,不仅是对计算机视觉技术在农业应用领域的一次创新探索,更是应对棕榈油生产挑战、推动可持续发展的重要举措。通过本研究的实施,期望能够为棕榈油产业的智能化、精准化管理提供有效的技术支持,助力实现经济效益与环境保护的双赢局面。

图片效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

数据集信息

数据集信息展示

在本研究中,我们采用了名为“Aura Palm Oil detection”的数据集,以支持改进YOLOv8-seg的Aura棕榈油果实分割系统的训练和评估。该数据集专门设计用于棕榈油产业,旨在提高对不同成熟度果实的识别和分割能力,从而推动农业自动化和智能化的发展。数据集包含四个主要类别,分别是“FROND”(棕榈叶)、“RIPE”(成熟果实)、“STOICK”(未成熟果实)和“UNRIPE”(青果),这些类别的划分不仅有助于提高模型的准确性,也为后续的农业管理和收获决策提供了重要依据。

在数据集的构建过程中,研究团队收集了大量的棕榈油树图像,涵盖了不同生长阶段、不同光照条件和不同环境背景下的棕榈油树。这些图像经过精心标注,确保每个类别的样本数量和质量都达到一定标准,以便于模型的训练。数据集中的每一张图像都配有详细的标注信息,包括目标的边界框和类别标签,这为YOLOv8-seg模型的训练提供了丰富的监督信号。

数据集的多样性是其一大亮点,包含了来自不同地理位置和气候条件下的棕榈油树图像,这使得模型在面对各种实际应用场景时,能够展现出更强的适应性和鲁棒性。特别是在农业生产中,棕榈油树的生长受环境因素的影响较大,因此,数据集的多样性能够有效提高模型对环境变化的敏感度,进而提升果实分割的准确性。

此外,数据集还考虑到了不同成熟度果实的特征差异。成熟果实(RIPE)通常呈现出鲜艳的颜色和较大的体积,而未成熟果实(UNRIPE)则颜色较浅且体积较小。棕榈叶(FROND)作为背景元素,提供了丰富的纹理和颜色变化,这为模型的训练增加了难度,但同时也提升了模型的学习能力。通过这种方式,数据集不仅仅是简单的图像集合,而是一个综合考虑了目标特征、环境因素和实际应用需求的复杂系统。

在模型训练过程中,我们将数据集划分为训练集、验证集和测试集,以确保模型的泛化能力和稳定性。训练集用于模型的学习,验证集用于调参和模型选择,而测试集则用于最终的性能评估。通过这种科学的划分方式,我们能够全面评估模型在不同数据条件下的表现,从而为后续的实际应用提供可靠的技术支持。

总之,“Aura Palm Oil detection”数据集为改进YOLOv8-seg的Aura棕榈油果实分割系统提供了坚实的基础。通过对数据集的深入分析和合理利用,我们期望能够实现更高效、更准确的果实分割,推动棕榈油产业的智能化进程,最终实现农业生产的可持续发展。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

核心代码

以下是经过简化和注释的核心代码部分,主要功能是自动估算YOLO模型的最佳批处理大小,以便在PyTorch中使用可用的CUDA内存。

import numpy as np
import torch
from ultralytics.utils import LOGGER, colorstr
from ultralytics.utils.torch_utils import profile

def check_train_batch_size(model, imgsz=640, amp=True):
“”"
检查YOLO训练的批处理大小。

参数:model (torch.nn.Module): 要检查批处理大小的YOLO模型。imgsz (int): 用于训练的图像大小。amp (bool): 如果为True,则使用自动混合精度(AMP)进行训练。返回:(int): 使用autobatch()函数计算的最佳批处理大小。
"""
with torch.cuda.amp.autocast(amp):return autobatch(model.train(), imgsz)  # 计算最佳批处理大小

def autobatch(model, imgsz=640, fraction=0.60, batch_size=16):
“”"
自动估算YOLO模型的最佳批处理大小,以使用可用CUDA内存的一部分。

参数:model (torch.nn.Module): 要计算批处理大小的YOLO模型。imgsz (int): 用作YOLO模型输入的图像大小,默认为640。fraction (float): 要使用的可用CUDA内存的比例,默认为0.60。batch_size (int): 如果检测到错误,则使用的默认批处理大小,默认为16。返回:(int): 最佳批处理大小。
"""
# 检查设备
prefix = colorstr('AutoBatch: ')
LOGGER.info(f'{prefix}计算图像大小为{imgsz}的最佳批处理大小')
device = next(model.parameters()).device  # 获取模型设备
if device.type == 'cpu':LOGGER.info(f'{prefix}未检测到CUDA,使用默认CPU批处理大小 {batch_size}')return batch_size# 检查CUDA内存
gb = 1 << 30  # 字节转GiB (1024 ** 3)
properties = torch.cuda.get_device_properties(device)  # 获取设备属性
total_memory = properties.total_memory / gb  # GiB总内存
reserved_memory = torch.cuda.memory_reserved(device) / gb  # GiB保留内存
allocated_memory = torch.cuda.memory_allocated(device) / gb  # GiB已分配内存
free_memory = total_memory - (reserved_memory + allocated_memory)  # GiB可用内存
LOGGER.info(f'{prefix}{device} ({properties.name}) {total_memory:.2f}G总, {reserved_memory:.2f}G保留, {allocated_memory:.2f}G已分配, {free_memory:.2f}G可用')# 评估批处理大小
batch_sizes = [1, 2, 4, 8, 16]  # 测试的批处理大小
try:img = [torch.empty(b, 3, imgsz, imgsz) for b in batch_sizes]  # 创建空图像张量results = profile(img, model, n=3, device=device)  # 评估模型性能# 拟合线性模型memory_usage = [x[2] for x in results if x]  # 提取内存使用情况p = np.polyfit(batch_sizes[:len(memory_usage)], memory_usage, deg=1)  # 一次多项式拟合optimal_batch_size = int((free_memory * fraction - p[1]) / p[0])  # 计算最佳批处理大小# 检查是否有失败的批处理大小if None in results:fail_index = results.index(None)  # 第一个失败的索引if optimal_batch_size >= batch_sizes[fail_index]:  # 如果最佳大小超过失败点optimal_batch_size = batch_sizes[max(fail_index - 1, 0)]  # 选择安全点# 检查最佳批处理大小是否在安全范围内if optimal_batch_size < 1 or optimal_batch_size > 1024:optimal_batch_size = batch_sizeLOGGER.info(f'{prefix}警告 ⚠️ 检测到CUDA异常,使用默认批处理大小 {batch_size}.')# 记录实际使用的批处理大小LOGGER.info(f'{prefix}使用批处理大小 {optimal_batch_size},可用内存占比 {free_memory:.2f}G/{total_memory:.2f}G ({(np.polyval(p, optimal_batch_size) + reserved_memory + allocated_memory) / total_memory * 100:.0f}%) ✅')return optimal_batch_size
except Exception as e:LOGGER.warning(f'{prefix}警告 ⚠️ 检测到错误: {e}, 使用默认批处理大小 {batch_size}.')return batch_size

代码说明:
check_train_batch_size:该函数用于检查YOLO模型的最佳批处理大小。它使用autobatch函数来计算最佳值,并支持自动混合精度(AMP)。

autobatch:该函数负责自动估算最佳批处理大小。它首先检查CUDA设备的可用内存,然后通过尝试不同的批处理大小来评估内存使用情况,并使用线性拟合来预测最佳批处理大小。

内存检查:通过获取设备的总内存、保留内存和已分配内存,计算出可用内存,并在日志中记录这些信息。

批处理大小评估:使用不同的批处理大小创建空图像张量,并通过profile函数评估模型的内存使用情况。然后通过线性拟合找到最佳批处理大小。

异常处理:在计算过程中,如果发生异常,将返回默认的批处理大小,并记录警告信息。

这个程序文件ultralytics/utils/autobatch.py主要用于在PyTorch中估算YOLO模型的最佳批处理大小,以便有效利用可用的CUDA内存。程序的核心功能是通过分析CUDA内存的使用情况,自动调整训练时的批处理大小,从而优化训练过程。

文件中首先导入了一些必要的库,包括deepcopy用于深拷贝模型,numpy用于数值计算,torch用于深度学习框架的操作,以及ultralytics中的一些工具函数和默认配置。

程序定义了两个主要函数:check_train_batch_size和autobatch。check_train_batch_size函数的作用是检查给定YOLO模型的最佳批处理大小。它接受模型、图像大小和是否使用自动混合精度(AMP)作为参数。在函数内部,使用torch.cuda.amp.autocast来启用AMP,并调用autobatch函数来计算最佳批处理大小。

autobatch函数是实现批处理大小自动调整的核心。它首先检查模型所在的设备,如果是CPU,则直接返回默认的批处理大小。如果是CUDA设备,则进一步检查CUDA的设置和内存使用情况。函数获取设备的总内存、已保留内存和已分配内存,并计算出可用的空闲内存。

接下来,程序定义了一组可能的批处理大小(1, 2, 4, 8, 16),并创建相应大小的空张量进行内存分析。通过调用profile函数,程序可以测量不同批处理大小下的内存使用情况。然后,使用一阶多项式拟合这些结果,以估算最佳的批处理大小。

如果在某些批处理大小下发生错误,程序会根据失败的索引选择一个安全的批处理大小。如果计算出的批处理大小不在合理范围内(小于1或大于1024),则会使用默认的批处理大小。最后,程序记录使用的批处理大小和实际的内存使用情况,并返回最佳批处理大小。

整个程序的设计旨在提高YOLO模型训练的效率,确保在不超出GPU内存限制的情况下,尽可能使用更大的批处理大小,从而加快训练速度。

11.5 ultralytics\models\sam\build.py
以下是经过简化和注释的核心代码部分,主要功能是构建不同大小的Segment Anything Model (SAM)模型。

import torch
from functools import partial
from ultralytics.utils.downloads import attempt_download_asset
from .modules.decoders import MaskDecoder
from .modules.encoders import ImageEncoderViT, PromptEncoder
from .modules.sam import Sam
from .modules.tiny_encoder import TinyViT
from .modules.transformer import TwoWayTransformer

def _build_sam(encoder_embed_dim,
encoder_depth,
encoder_num_heads,
encoder_global_attn_indexes,
checkpoint=None,
mobile_sam=False):
“”“构建指定的SAM模型架构。”“”

# 定义提示嵌入维度和图像相关参数
prompt_embed_dim = 256
image_size = 1024
vit_patch_size = 16
image_embedding_size = image_size // vit_patch_size  # 计算图像嵌入大小# 根据是否为移动SAM选择不同的图像编码器
image_encoder = (TinyViT(img_size=1024,in_chans=3,num_classes=1000,embed_dims=encoder_embed_dim,depths=encoder_depth,num_heads=encoder_num_heads,window_sizes=[7, 7, 14, 7],mlp_ratio=4.0,drop_rate=0.0,drop_path_rate=0.0,use_checkpoint=False,mbconv_expand_ratio=4.0,local_conv_size=3,
) if mobile_sam else ImageEncoderViT(depth=encoder_depth,embed_dim=encoder_embed_dim,img_size=image_size,mlp_ratio=4,norm_layer=partial(torch.nn.LayerNorm, eps=1e-6),num_heads=encoder_num_heads,patch_size=vit_patch_size,qkv_bias=True,use_rel_pos=True,global_attn_indexes=encoder_global_attn_indexes,window_size=14,out_chans=prompt_embed_dim,
))# 创建SAM模型,包括图像编码器、提示编码器和掩码解码器
sam = Sam(image_encoder=image_encoder,prompt_encoder=PromptEncoder(embed_dim=prompt_embed_dim,image_embedding_size=(image_embedding_size, image_embedding_size),input_image_size=(image_size, image_size),mask_in_chans=16,),mask_decoder=MaskDecoder(num_multimask_outputs=3,transformer=TwoWayTransformer(depth=2,embedding_dim=prompt_embed_dim,mlp_dim=2048,num_heads=8,),transformer_dim=prompt_embed_dim,iou_head_depth=3,iou_head_hidden_dim=256,),pixel_mean=[123.675, 116.28, 103.53],  # 图像像素均值pixel_std=[58.395, 57.12, 57.375],      # 图像像素标准差
)# 如果提供了检查点,则加载模型权重
if checkpoint is not None:checkpoint = attempt_download_asset(checkpoint)  # 尝试下载检查点with open(checkpoint, 'rb') as f:state_dict = torch.load(f)  # 加载权重sam.load_state_dict(state_dict)  # 将权重加载到模型中sam.eval()  # 设置模型为评估模式
return sam  # 返回构建的SAM模型

代码说明:
导入必要的库:导入PyTorch和其他相关模块。
_build_sam函数:核心函数,用于构建SAM模型。根据输入参数配置不同的编码器和解码器。
图像编码器选择:根据是否为移动版本选择不同的图像编码器(TinyViT或ImageEncoderViT)。
SAM模型构建:创建Sam对象,包含图像编码器、提示编码器和掩码解码器。
加载检查点:如果提供了模型检查点,则下载并加载权重。
模型评估模式:设置模型为评估模式,准备进行推理。
这个程序文件是用于构建和返回“Segment Anything Model”(SAM)模型的。文件中包含了多个函数,每个函数负责构建不同大小的SAM模型(如h、l、b等),以及一个用于构建移动版本的SAM模型(Mobile-SAM)。程序首先导入了必要的库和模块,包括PyTorch和一些自定义的模块。

在文件的开头,定义了几个构建函数,例如build_sam_vit_h、build_sam_vit_l和build_sam_vit_b,这些函数分别用于构建不同参数配置的SAM模型。每个函数都调用了一个私有函数_build_sam,并传入相应的参数,比如编码器的嵌入维度、深度、头数等。

_build_sam函数是核心部分,它根据传入的参数构建具体的SAM模型架构。函数内部首先定义了一些固定的参数,如提示嵌入维度、图像大小和补丁大小。接着,根据是否构建移动版本的SAM,选择使用不同的图像编码器(TinyViT或ImageEncoderViT)。然后,创建了SAM模型的主要组件,包括图像编码器、提示编码器和掩码解码器。

如果提供了检查点(checkpoint),程序会尝试下载并加载模型的状态字典,以便恢复模型的权重。最后,模型被设置为评估模式并返回。

文件还定义了一个字典samm_model_map,将模型文件名映射到相应的构建函数。build_sam函数根据给定的检查点名称,查找并调用相应的构建函数,构建出所需的SAM模型。如果提供的检查点不在支持的模型列表中,程序会抛出一个文件未找到的异常。

整体来看,这个程序文件提供了一种灵活的方式来构建不同配置的SAM模型,适用于各种应用场景。

12.系统整体结构(节选)
程序整体功能和构架概括
该程序是Ultralytics YOLO和Segment Anything Model(SAM)实现的一部分,主要用于目标检测和图像分割任务。程序的整体架构模块化,涵盖了模型训练、验证、损失计算、配置管理和自动批处理大小调整等功能。每个模块或文件负责特定的功能,确保代码的可维护性和可扩展性。

损失函数模块 (loss.py): 提供多种损失函数的实现,支持目标检测、分割和关键点检测任务。
配置管理模块 (init.py): 处理模型的配置和命令行接口,允许用户灵活设置训练参数。
验证模块 (val.py): 负责模型在验证集上的性能评估,计算各种指标并生成结果。
自动批处理模块 (autobatch.py): 根据可用的CUDA内存自动调整训练时的批处理大小,以优化训练效率。
模型构建模块 (build.py): 提供构建不同配置的SAM模型的功能,支持加载预训练权重。
文件功能整理表
文件路径 功能描述
ultralytics/utils/loss.py 实现多种损失函数,用于目标检测、分割和关键点检测。
ultralytics/cfg/init.py 处理模型配置和命令行接口,支持训练参数的灵活设置。
ultralytics/models/yolo/detect/val.py 负责模型验证,计算性能指标并生成结果。
ultralytics/utils/autobatch.py 自动调整训练时的批处理大小,以优化CUDA内存使用。
ultralytics/models/sam/build.py 构建不同配置的Segment Anything Model(SAM),支持加载预训练权重。
这个表格清晰地展示了每个文件的功能,便于理解程序的整体架构和各个模块之间的关系。

13.图片、视频、摄像头图像分割Demo(去除WebUI)代码
在这个博客小节中,我们将讨论如何在不使用WebUI的情况下,实现图像分割模型的使用。本项目代码已经优化整合,方便用户将分割功能嵌入自己的项目中。 核心功能包括图片、视频、摄像头图像的分割,ROI区域的轮廓提取、类别分类、周长计算、面积计算、圆度计算以及颜色提取等。 这些功能提供了良好的二次开发基础。

核心代码解读
以下是主要代码片段,我们会为每一块代码进行详细的批注解释:

import random
import cv2
import numpy as np
from PIL import ImageFont, ImageDraw, Image
from hashlib import md5
from model import Web_Detector
from chinese_name_list import Label_list

根据名称生成颜色

def generate_color_based_on_name(name):

计算多边形面积

def calculate_polygon_area(points):
return cv2.contourArea(points.astype(np.float32))

绘制中文标签

def draw_with_chinese(image, text, position, font_size=20, color=(255, 0, 0)):
image_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(image_pil)
font = ImageFont.truetype(“simsun.ttc”, font_size, encoding=“unic”)
draw.text(position, text, font=font, fill=color)
return cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)

动态调整参数

def adjust_parameter(image_size, base_size=1000):
max_size = max(image_size)
return max_size / base_size

绘制检测结果

def draw_detections(image, info, alpha=0.2):
name, bbox, conf, cls_id, mask = info[‘class_name’], info[‘bbox’], info[‘score’], info[‘class_id’], info[‘mask’]
adjust_param = adjust_parameter(image.shape[:2])
spacing = int(20 * adjust_param)

if mask is None:x1, y1, x2, y2 = bboxaim_frame_area = (x2 - x1) * (y2 - y1)cv2.rectangle(image, (x1, y1), (x2, y2), color=(0, 0, 255), thickness=int(3 * adjust_param))image = draw_with_chinese(image, name, (x1, y1 - int(30 * adjust_param)), font_size=int(35 * adjust_param))y_offset = int(50 * adjust_param)  # 类别名称上方绘制,其下方留出空间
else:mask_points = np.concatenate(mask)aim_frame_area = calculate_polygon_area(mask_points)mask_color = generate_color_based_on_name(name)try:overlay = image.copy()cv2.fillPoly(overlay, [mask_points.astype(np.int32)], mask_color)image = cv2.addWeighted(overlay, 0.3, image, 0.7, 0)cv2.drawContours(image, [mask_points.astype(np.int32)], -1, (0, 0, 255), thickness=int(8 * adjust_param))# 计算面积、周长、圆度area = cv2.contourArea(mask_points.astype(np.int32))perimeter = cv2.arcLength(mask_points.astype(np.int32), True)......# 计算色彩mask = np.zeros(image.shape[:2], dtype=np.uint8)cv2.drawContours(mask, [mask_points.astype(np.int32)], -1, 255, -1)color_points = cv2.findNonZero(mask)......# 绘制类别名称x, y = np.min(mask_points, axis=0).astype(int)image = draw_with_chinese(image, name, (x, y - int(30 * adjust_param)), font_size=int(35 * adjust_param))y_offset = int(50 * adjust_param)# 绘制面积、周长、圆度和色彩值metrics = [("Area", area), ("Perimeter", perimeter), ("Circularity", circularity), ("Color", color_str)]for idx, (metric_name, metric_value) in enumerate(metrics):......return image, aim_frame_area

处理每帧图像

def process_frame(model, image):
pre_img = model.preprocess(image)
pred = model.predict(pre_img)
det = pred[0] if det is not None and len(det)
if det:
det_info = model.postprocess(pred)
for info in det_info:
image, _ = draw_detections(image, info)
return image

if name == “main”:
cls_name = Label_list
model = Web_Detector()
model.load_model(“./weights/yolov8s-seg.pt”)

# 摄像头实时处理
cap = cv2.VideoCapture(0)
while cap.isOpened():ret, frame = cap.read()if not ret:break......# 图片处理
image_path = './icon/OIP.jpg'
image = cv2.imread(image_path)
if image is not None:processed_image = process_frame(model, image)......# 视频处理
video_path = ''  # 输入视频的路径
cap = cv2.VideoCapture(video_path)
while cap.isOpened():ret, frame = cap.read()......

源码文件

在这里插入图片描述

源码获取

欢迎大家点赞、收藏、关注、评论啦 、查看👇🏻获取联系方式👇🏻

http://www.dtcms.com/a/486658.html

相关文章:

  • 蛋白表达标签:提升重组蛋白研究与生产的关键工具
  • 网站备案编号查询wordpress 集成paypal
  • 数学口算练习抖音快手微信小程序看广告流量主开源
  • 【开题答辩过程】以《泰山珍稀动植物信息管理平台的设计与实现》为例,不会开题答辩的可以进来看看
  • wordpress 淘宝客页面seo网络培训班
  • 哪些公司做网站wordpress单选框php
  • 什么是程序计数器?
  • GEO实战之GEO 在营销生态中的定位:社交媒体、PR、内容营销的整合策略
  • 10.13 Tabs选项卡布局
  • 深圳比较好网站制作公司有哪些设置wordpress网页私有
  • seo1视频发布会优化关键词的公司
  • Uniapp微信小程序开发:onPullDownRefresh
  • 如何优化CMS的缓存机制?
  • h5游戏免费下载:Emoji自定义表情编辑器
  • AbMole小课堂丨重组R-spondin-3(RSPO3)的作用机理及其在类器官和干细胞研究中的应用
  • springboot 实现websocket通信
  • 深度学习实战:python动物识别分类检测系统 计算机视觉 Django框架 CNN算法 深度学习 卷积神经网络 TensorFlow 毕业设计(建议收藏)✅
  • app使用什么做的网站wordpress自动保存编辑器图片
  • 静态网页发布到wordpress河南关键词优化搜索
  • 从递归到迭代吃透树的层次——力扣104.二叉树的最大深度
  • 基于无监督深度学习方法的非迭代式、不确定性感知的磁共振成像肝脏脂肪定量评估|文献速递-文献分享
  • 如何能把网站做的更大宿主选择 网站建设
  • django rest framework:从零开始搭建RESTful API
  • springboot3加密配置文件的值
  • 抗干扰汽车微型网络(RAMN)开源测试平台的设计
  • wordpress 评测谷歌seo优化什么意思
  • C++手撕无锁线程池
  • kettle Spoon.bat启动报错:could not create the java virtual machine
  • 连云港做网站企业网站建设简单合同
  • DB-GPT AWEL工作流引擎深度解析