YOLO实践

news2024/11/15 7:55:29

一. 环境安装

参考视频

  1. Pytorch环境安装细节
  • pytorch安装:一个单独的环境中,能使用pip就尽量使用pip,实在有问题的情况,例如没有合适的编译好的系统版本的安装包,再使用conda进行安装,不要来回混淆
  • CUDA是否要安装:如果只需要训练、简单推理,则无需单独安装cUDA,直接安装pytorch;如果有部署需求,例如导出TensorRT模型,则需要进行CUDA安装
  • Pytorch安装注意事项:必须使用官网的命令进行安装,否则安装的是cpu的版本
  1. 正确使用windows终端:
  • 使用cmd,而不是powershell(无法激活环境)。
  • 在其他软件,如Pycharm,vscode中也要注意!
  1. 可能出现的问题
  • Arial.ttf字体文件无法下载
    • 手动下载,放到对应的位置,windows下的目录是:~/AppData/Roaming/Ultralytics
  • 页面文件太小,无法完成操作
    • 调整训练参数中的workers,设置为1
    • 修改虚拟内存,将环境安装位置所在的盘,设置一个较大的参数
  • ‘Upsample’ object has no attribute ‘recompute _scale_factor’
    • pytorch版本过高导致,可以选择降版本,1.8.2目前是不会报错的版本
    • 如不想降低版本,可以修改pytorch源码,打开报错的unsampling.py,删除
      recompute_scale_factor这个参数

设置电脑虚拟内存:
在这里插入图片描述
在这里插入图片描述

二 .数据集制作

数据转换代码数据格式转化关键根据相应的文件格式解析处class以及bbox

  1. voc2yolo
def convert_xml_to_txt_format(xml_file_path: str, class_list: list) -> list:
    """
    将单个xml文件转换为txt格式。

    参数:
    xml_file_path (str): XML文件的路径。
    class_list (list): 包含所有类别的列表。

    返回:
    list: 包含YOLO格式的标签列表。
    """
    # 根据class_list构建class_to_id_map
    class_to_id_map = {class_name: index for index, class_name in enumerate(class_list)}

    tree = ET.parse(xml_file_path)
    root = tree.getroot()

    width = int(root.find('.//size/width').text)
    height = int(root.find('.//size/height').text)

    txt_format = []
    for obj in root.findall('.//object'):
        class_name = obj.find('name').text
        if class_name not in class_to_id_map:
            continue
        class_id = class_to_id_map[class_name]
        bbox = obj.find('bndbox')
        xmin = float(bbox.find('xmin').text)
        ymin = float(bbox.find('ymin').text)
        xmax = float(bbox.find('xmax').text)
        ymax = float(bbox.find('ymax').text)

        x_center = (xmin + xmax) / 2 / width
        y_center = (ymin + ymax) / 2 / height
        bbox_width = (xmax - xmin) / width
        bbox_height = (ymax - ymin) / height

        txt_format.append(f"{class_id} {x_center} {y_center} {bbox_width} {bbox_height}")

    return txt_format



def write_yolo_format_labels_to_file(yolo_labels_dir: str, yolo_label_file: str, yolo_content: list):
    """
    将YOLO格式的标签写入到文件。

    参数:
    yolo_labels_dir (str): YOLO标签目录的路径。
    yolo_label_file (str): YOLO标签文件的名称。
    yolo_content (list): 包含YOLO格式标签的列表。
    """
    yolo_label_path = os.path.join(yolo_labels_dir, yolo_label_file)
    with open(yolo_label_path, 'w') as f:
        for line in yolo_content:
            f.write(line + '\n')


def convert_voc2yolo(voc_annotations_dir: str, yolo_labels_dir: str, class_list: list):
    """
    将VOC格式的XML注释转换为YOLO格式的注释。

    参数:
    voc_annotations_dir (str): VOC注释目录的路径。
    yolo_labels_dir (str): YOLO标签目录的路径。
    class_list (list): 类别名称的list。
    """
    if not os.path.exists(yolo_labels_dir):
        os.makedirs(yolo_labels_dir)

    for xml_file in os.listdir(voc_annotations_dir):
        if xml_file.endswith('.xml'):
            yolo_label_file = xml_file.replace('.xml', '.txt')
            xml_file_path = os.path.join(voc_annotations_dir, xml_file)

            yolo_content = convert_xml_to_txt_format(xml_file_path,class_list)
            write_yolo_format_labels_to_file(yolo_labels_dir, yolo_label_file, yolo_content)
    with open(os.path.join(yolo_labels_dir,"classes.txt"),"w",encoding='utf-8') as f:
        for c in class_list:
            f.write(c+'\n')
if __name__ == "__main__":
    dataset_dir = "./dataset"
    labels_voc_dir = "labels_voc"
    labels_yolo_dir = "labels_yolo"
    class_names = ["橘子", "香蕉", "草莓"]
    convert_voc2yolo(labels_voc_dir,labels_yolo_dir,class_names)
  1. yolo2voc

def convert_txt_to_xml_format(txt_file_path: str, voc_file_path: str, class_names: list, images_dir: str):
    """
    将单个txt文件转换为xml格式。

    参数:
    txt_file_path (str): YOLO标签文件txt的路径。
    voc_file_path (str): XML文件的路径。
    class_names (list): 包含类别名称的列表。
    images_dir (str): 包含图像文件的目录路径。
    """
    with open(txt_file_path, 'r') as file:
        lines = file.readlines()

    # 获取txt文件的文件名,然后去掉后缀
    txt_filename = os.path.basename(txt_file_path)
    txt_filename_without_ext = os.path.splitext(txt_filename)[0]

    # 使用glob库在images_dir下搜索与txt文件同名的图片文件
    pattern = os.path.join(images_dir, f"{txt_filename_without_ext}.*")
    matching_files = glob.glob(pattern)

    if not matching_files:
        raise FileNotFoundError(f"No matching image found for {txt_filename_without_ext} in {images_dir}")

    img_file = matching_files[0]  # 假设找到的第一个匹配文件就是正确的图片

    # 从与YOLO标签同名的图像文件中读取宽度和高度
    img = Image.open(img_file)
    width, height = img.size

    root = ET.Element("annotation")

    folder = ET.SubElement(root, "folder")
    folder.text = os.path.basename(images_dir)

    filename = ET.SubElement(root, "filename")
    filename.text = os.path.basename(img_file)

    path = ET.SubElement(root, "path")
    path.text = os.path.abspath(img_file)

    source = ET.SubElement(root, "source")
    database = ET.SubElement(source, "database")
    database.text = "Unknown"

    size = ET.SubElement(root, "size")
    ET.SubElement(size, "width").text = str(width)
    ET.SubElement(size, "height").text = str(height)
    ET.SubElement(size, "depth").text = "3"

    segmented = ET.SubElement(root, "segmented")
    segmented.text = "0"

    for line in lines:
        values = line.strip().split()
        if len(values) < 5:
            continue

        class_id, x_center, y_center, bbox_width, bbox_height = map(float, values)

        object = ET.SubElement(root, "object")
        name = ET.SubElement(object, "name")
        name.text = class_names[int(class_id)]

        pose = ET.SubElement(object, "pose")
        pose.text = "Unspecified"

        truncated = ET.SubElement(object, "truncated")
        truncated.text = "0"

        difficult = ET.SubElement(object, "difficult")
        difficult.text = "0"

        bndbox = ET.SubElement(object, "bndbox")
        xmin = int((x_center - bbox_width / 2) * width)
        ymin = int((y_center - bbox_height / 2) * height)
        xmax = int((x_center + bbox_width / 2) * width)
        ymax = int((y_center + bbox_height / 2) * height)
        ET.SubElement(bndbox, "xmin").text = str(xmin)
        ET.SubElement(bndbox, "ymin").text = str(ymin)
        ET.SubElement(bndbox, "xmax").text = str(xmax)
        ET.SubElement(bndbox, "ymax").text = str(ymax)

    # 使用prettify函数来格式化输出
    pretty_xml_as_string = prettify(root)
    with open(voc_file_path, 'w', encoding='utf-8') as f:
        f.write(pretty_xml_as_string)


def prettify(elem):
    from xml.dom import minidom
    """Return a pretty-printed XML string for the Element."""
    rough_string = ET.tostring(elem, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent="	")


def convert_yolo2voc(yolo_labels_dir: str, voc_labels_dir: str, images_dir: str, class_list: list):
    """
    将YOLO格式的标签文件夹转换为VOC格式的XML注释文件夹。

    参数:
    yolo_labels_dir (str): YOLO标签文件夹的路径。
    voc_labels_dir (str): 转换后的VOC XML文件夹的路径。
    images_dir (str): 包含图像文件的目录路径。
    class_names (list): 包含类别名称的列表。
    """
    # 确保voc_labels_dir存在
    if not os.path.exists(voc_labels_dir):
        os.makedirs(voc_labels_dir)

    # 遍历YOLO标签文件夹
    for label_file in os.listdir(yolo_labels_dir):
        if label_file.endswith(".txt"):
            yolo_file_path = os.path.join(yolo_labels_dir, label_file)
            voc_file_path = os.path.join(voc_labels_dir, label_file.replace(".txt", ".xml"))
            convert_txt_to_xml_format(yolo_file_path, voc_file_path, class_list, images_dir)
if __name__ == "__main__":
    dataset_dir = "./dataset"
    labels_voc_dir = "labels_voc"
    labels_yolo_dir = "labels_yolo"
    class_names = ["橘子", "香蕉", "草莓"]
    convert_yolo2voc(labels_yolo_dir,labels_voc_dir,"imgs",class_names)
    
  1. Json转Yolo

提示可以采用 data_df = pd.read_json(json_path),然后利用pandas的语法进行提取bbox和class_id

  1. 划分数据集
def split_dataset(images_origin_dir: str, labels_origin_dir: str, dataset_dir: str, train_ratio: float = 0.8):
    """
    将原始数据集分割为训练集和验证集,并将相应的文件复制到新的文件夹中。

    参数:
    images_origin (str): 存放所有图片文件夹路径
    labels_origin (str): 存放所有标签文件夹路径。
    dataset_dir (str): 新的数据集根目录。
    train_ratio (float, optional): 训练集的比例,默认为 0.8。
    """
    images_dir = os.path.join(dataset_dir, "images")
    labels_dir = os.path.join(dataset_dir, "labels")
    train_dir = os.path.join(images_dir, "train")
    val_dir = os.path.join(images_dir, "val")

    # 创建训练集和验证集的文件夹
    os.makedirs(train_dir, exist_ok=True)
    os.makedirs(val_dir, exist_ok=True)
    os.makedirs(os.path.join(labels_dir, "train"), exist_ok=True)
    os.makedirs(os.path.join(labels_dir, "val"), exist_ok=True)

    image_files=[]
    for file in os.listdir(images_origin_dir):
        if file.endswith(".jpg") or file.endswith(".png") or file.endswith(".jpeg"):
            image_files.append(os.path.join(images_origin_dir, file))

    # 使用 random.sample 函数进行随机划分
    random.seed(42)  # 设置随机种子
    train_images = random.sample(image_files, k=int(len(image_files) * train_ratio))
    val_images = [f for f in image_files if f not in train_images]

    # 复制文件到训练集和验证集文件夹
    for subset, images in [('train', train_images), ('val', val_images)]:
        for image_file in images:
            # 复制图片文件
            target_image_path = os.path.join(images_dir, subset, os.path.basename(image_file))
            shutil.copyfile(image_file, target_image_path)

            # 复制标签文件
            label_file = os.path.splitext(os.path.basename(image_file))[0] + '.txt'
            label_path = os.path.join(labels_origin_dir, label_file)
            if os.path.exists(label_path):
                target_label_path = os.path.join(labels_dir, subset, label_file)
                shutil.copyfile(label_path, target_label_path)

    print(f"训练集大小: {len(train_images)}")
    print(f"验证集大小: {len(val_images)}")
 if __name__ == "__main__":
    dataset_dir = "./dataset"
    labels_voc_dir = "labels_voc"
    labels_yolo_dir = "labels_yolo"
    class_names = ["橘子", "香蕉", "草莓"]
    split_dataset("imgs",labels_yolo_dir,dataset_dir)

三.模型使用

  1. YOLOV5 模型调用(本地)
# Model
model = torch.hub.load('.', 'custom', path=r'best.pt',source='local')

.: 本地的yolov5的根目录
source=“local”:从本地调用该模型

  1. YOLOV8 模型调用
# Load the YOLOv8 model
model = YOLO("yolov8n.pt")  # Make sure the model file is in the correct path

如果本地不存在yolov8n.pt,则从网上下载

四. 结果处理

.xyxy():即为[x_min,y_min,x_max,y_max]

  1. YOLOV5 参考博客
import torch
model = torch.hub.load('.', 'yolov5s', source='local') 
im = r'data\images\bus.jpg'  # file, Path, PIL.Image, OpenCV, nparray, list`
results = model(im)  # inference
# results.crop()  # or .show(), .save(), .crop(), .pandas(), etc.

results是一个Detections对象,主要有以下方法:

# results.crop()  # or .show(), .save(), .crop(), .pandas(), etc.
results.xyxy[0]   #Tensor类型
results.pandas().xyxy[0]  #转为pandas格式
results.print()  #控制台显示
frame = results.render()[0]  # 将模型的输出绘制回帧上

另外:想要个性化输出,限定类别

# 将检测结果转换为Pandas DataFrame格式
detections_df = results.pandas().xyxy[0]

# 筛选出标签名为'person'的所有检测结果
person_detections = detections_df[detections_df['name'] == 'person'].to_numpy()

# 遍历检测到的人
for detection in person_detections:
    label_name = detection[6]  # 获取标签名
    bbox = detection[:4].astype('int')  # 获取边界框坐标并转换为整数

    # 在图像上绘制边界框
    cv.rectangle(image_temp, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (0, 0, 255), 3)

    # 在边界框左上角添加标签名
    cv.putText(image_temp, label_name, (bbox[0] - 10, bbox[1] - 10), cv.FONT_ITALIC, 1, (0, 255, 0), 2)

  1. YOLOV8

建议:ultalytics源码安装,在ultralytics的根目录下pip install -e . -e指的是本地可编辑,下载该库时不会直接下载到site-packages下,而是以链接的方式链接到源码的根目录下

from ultralytics import YOLO
model=YOLO("yolov8n-seg.pt")
results=model(r"images\bus.jpg")

这里的results是list类型。

for idx, result in enumerate(results):
        boxes = result.boxes  # Boxes object for bounding box outputs
        masks = result.masks  # Masks object for segmentation masks outputs
        keypoints = result.keypoints  # Keypoints object for pose outputs
        probs = result.probs  # Probs object for classification outputs
        obb = result.obb  # Oriented boxes object for OBB outputs

        if len(boxes.cls) == 0:
            continue
        
        xyxy = boxes.xyxy.data.cpu().numpy().round()
        cls = boxes.cls.data.cpu().numpy().round()
        conf = boxes.conf.data.cpu().numpy()

另外:如果想要个性化输出,可以使用 patched_yolo_infer库(我只验证了YOLOV8,其它的YOLO系列不知道)

import cv2
from ultralytics import YOLO
from patched_yolo_infer import visualize_results_usual_yolo_inference


# Load the image
img_path = 'images/bus.jpg'
img = cv2.imread(img_path)

img=visualize_results_usual_yolo_inference(
        img,
        model,
        conf=0.4,
        iou=0.7,
        show_classes_list=[0], #Whether to perform instance segmentation. Default is False.
        segment=True,          #是否分割
        thickness=5,
        show_boxes=False,
        fill_mask=False,      
        alpha=0.7,			#The transparency of filled masks. Default is 0.3.
        show_class=False,
        delta_colors=25,     #The random seed offset for color variation. Default is 0.
        inference_extra_args={'retina_masks':True}, #increase the accuracy of the contours
        return_image_array=True
    )

:Pyme组件

import cv2 as cv
from PIL import ImageTk, Image
class VideoPlayer():
    def __init__(self,elementName,video_source=0):
        super().__init__()
        self.video_source = video_source
        self.vid = cv.VideoCapture(self.video_source)
        self.label= Fun.GetElement(uiName,elementName)  #如果想要替换为tk,这里直接注入label对象
        self.delay = 15
        self.paused = False  # 暂停状态标志
        self.update()
        
    def update(self):
        # 检查是否成功读取帧
        if self.vid.isOpened():
            if not self.paused:
                ret, frame = self.vid.read()
                if ret:
                    frame = self.process_frame(frame)
                    # 转换为Image
                    self.photo = ImageTk.PhotoImage(image=Image.fromarray(frame))
                    self.label.config(image=self.photo)
                else:
                    self.vid.release()
                    return
        self.label.after(self.delay, self.update)
        
    def process_frame(self, frame):
        #convert BGR to RGB
        frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
        return frame
    
    def pause(self):
        self.paused = not self.paused
        return self.paused
    
    def stop(self):
        self.vid.release()

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

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

相关文章

上网行为管理系统的功能有哪些(员工全网行为管理解决方案)

员工在工作中的上网行为日益多样化&#xff0c;这不仅带来了工作效率的提升&#xff0c;也带来了诸多管理上的挑战。 网络黑客攻击、数据泄露、非工作相关活动的占用带宽等问题层出不穷&#xff0c;对企业的信息安全和运营效率构成了严重威胁。 因此&#xff0c;上网行为管理…

HIC-YOLOv5:改进的YOLOv5用于小对象检测

HIC-YOLOv5: Improved YOLOv5 For Small Object Detection 摘要 小目标检测是物体检测领域的一大难点。已有的一些工作对这一任务提出了改进&#xff0c;例如增加若干个注意块或改变特征融合网络的整体结构。然而&#xff0c;这些模型的计算开销很大&#xff0c;使得部署实时目…

Leetcode面试经典150题-136.只出现一次的数字

解法都在代码里&#xff0c;不懂就留言或者私信 这个题不知道为啥会考&#xff0c;过于简单了&#xff0c;我解题写注释用了两分钟不到&#xff0c;5行代码。。。 class Solution {public int singleNumber(int[] nums) {/**这个题目确实时间的题&#xff0c;根据位运算法则我…

公安监所智慧监管解决方案

1. 项目背景与政策解读 《智慧监管行业背景》部分强调了国家关于推进智慧监管建设的指导意见&#xff0c;以及特定省份发布的“智慧新监管”建设规范&#xff0c;旨在实现监所管理的规范化、标准化和信息化。 2. 监所建设目标 根据政策要求&#xff0c;监所建设内容涵盖数字…

无人机之云台的作用

无人机云台在无人机技术中扮演着至关重要的角色&#xff0c;其作用主要体现在以下几个方面&#xff1a; 一、 确保拍摄稳定性 防抖动&#xff1a;无人机在飞行过程中&#xff0c;尤其是在复杂环境下&#xff0c;如遇到风力干扰或进行高速飞行时&#xff0c;机身容易产生震动和…

MySQL数据库---JDBC编程

1.目录 目录 1. 数据库编程的必备条件 2. Java的数据库编程&#xff1a;JDBC 3.安装工作JDBC&#xff1a; 1)使用经典版找到对应版本下载 2)点击Files栏目的jar 3)用文件夹打开 4)一直点进去会得到此界面 4.环境配置 1)下载 jar 2)把jar导入到自己的项目中. a)先在项…

算法训练营——day1数组二分查找

数组是存放在连续空间上的相同数据类型的集合。 注意&#xff1a;下标从0开始&#xff1b;内存空间连续。 正因为数组的内存地址空间连续&#xff0c;所以在删除、添加元素的时候需要移动其他元素。 数组的元素不能删除&#xff0c;只能覆盖&#xff01; 二维数组特殊 在C中&…

多目标应用:基于NSGA3的移动机器人路径规划研究(提供MATLAB代码)

一、机器人路径规划介绍 移动机器人&#xff08;Mobile robot&#xff0c;MR&#xff09;的路径规划是 移动机器人研究的重要分支之&#xff0c;是对其进行控制的基础。根据环境信息的已知程度不同&#xff0c;路径规划分为基于环境信息已知的全局路径规划和基于环境信息未知或…

Python函数(11自定义模块第三方模块内置模块)

Python基础语法文章导航&#xff1a; Python基础&#xff08;01初识数据类型&变量&#xff09;Python基础&#xff08;02条件&循环语句&#xff09;Python基础&#xff08;03字符串格式化&运算符&进制&编码&#xff09;Python基础&#xff08;04 基础练习…

定时器方案:时间表盘

目录 一&#xff1a;前言 二&#xff1a;手搓时间表盘 1、任务结点&#xff0c;层级&#xff0c;表盘的结构体 2、表盘的初始化 3、添加定时任务 4、删除定时任务 5、检查任务是否超时 6、清空任务 一&#xff1a;前言 我之前有两篇文章是写定时器方案的&#xff0c;大家…

智菜谱推|基于SprinBoot+vue的智能菜谱推荐系统(源码+数据库+文档)

智能菜谱推荐系统 基于SprinBootvue的智能菜谱推荐系统 一、前言 二、系统设计 三、系统功能设计 系统功能实现 管理员功能模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂…

【开源免费】基于SpringBoot+Vue.JS渔具租赁系统(JAVA毕业设计)

本文项目编号 T 005 &#xff0c;文末自助获取源码 \color{red}{T005&#xff0c;文末自助获取源码} T005&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 渔…

低空经济概念火爆:无人机飞手人才培养先行

随着科技的飞速发展&#xff0c;低空经济作为新兴的经济形态&#xff0c;正以前所未有的速度崛起&#xff0c;成为推动产业升级和经济发展的新引擎。无人机作为低空经济的重要组成部分&#xff0c;其应用领域已从最初的军事侦察、航拍扩展到农业植保、物流配送、环境监测、应急…

使用corrplot绘制行、列不同,且带有p值显著性标注的相关系数图

导读&#xff1a; 相关系数衡量两个变量之间的线性关系&#xff0c;通常以N*N的矩阵形式展示。例如样品vs样品&#xff0c;或者基因vs基因的相关性。本文介绍了使用corrplot R包绘制M*N的相关系数矩阵&#xff0c;例如M个基因表达与N个代谢物信号间的相关性&#xff0c;同时带…

国产芯片+国产操作系统打造办公系统

在《使用国产操作系统作为开发系统》一文中&#xff0c;我介绍了将开发系统从 Ubuntu 替换为 Deepin 系统的过程。经过一个多月的使用&#xff0c;Deepin 系统已然成为我的主力开发平台&#xff0c;其顺手程度让我对国产操作系统的信心大增。于是&#xff0c;我开始将目光瞄向公…

顶级开源许可证详解

目录 软件许可证类型&#xff1a;版权左派和宽容型 顶级开源许可证详解 GNU 通用公共许可证 (GPL) Apache 许可证 Microsoft 公共许可证 (Ms-PL) 伯克利软件发行版 (BSD) 通用开发和分发许可证 (CDDL) Eclipse 公共许可证 (EPL) MIT 许可证 了解你的开源许可证&#…

java编辑器——IntelliJ IDEA

java编辑器有两种选择——IntelliJ IDEA和VsCode。其中IntelliJ IDEA现在是企业用的比较多的&#xff0c;是专门为java设计的&#xff0c;而VsCode则是通过插件来实现Java编辑的。 1.IntelliJ IDEA 官网下载链接&#xff1a;https://www.jetbrains.com/idea/ 注意选择社区版…

AWS-亚马逊网络服务(基础服务)-AWS 定价计算器-概述与动手部署:

让我们来概述并亲身实践如何使用 AWS 定价计算器来计算 概述&#xff1a; AWS 定价计算器是 Amazon Web Services (AWS) 提供的基于 Web 的工具&#xff0c;可帮助用户估算其特定用例的 AWS 服务成本。欢迎来到雲闪世界。 它允许客户建模他们的基础设施并根据他们打算使用的…

【AI 绘画】更快?更省显存?支持 FLUX?使用绘世启动器安装 SD WebUI Forge

使用绘世启动器安装 SD WebUI Forge 下载绘世启动器 绘世启动器下载地址1&#xff1a;https://gitee.com/licyk/term-sd/releases/download/archive/hanamizuki.exe 绘世启动器下载地址2&#xff1a;https://www.bilibili.com/video/BV1ne4y1V7QU 新建一个文件夹取名sd-webui-…

中仕公考怎么样?公务员考试什么时候补录?

公务员考试补录的时间和方法通常因地区和职位的不同有所区别&#xff0c;一般来说&#xff0c;这一过程会在面试、体检和考核环节完成后启动。 如果在招录过程中出现职位空缺或者并未全部招满的情况&#xff0c;就会进行补录。用人单位会通过其官方或公告形式公布相关信息&…