yolo-nas对自定义数据集进行训练,测试详解 香烟数据集 处理损坏的图片数据 对网络摄像头,视频,图片预测

news2024/11/28 7:46:20

yolov5格式的香烟数据集

https://download.csdn.net/download/qq_42864343/88110620?spm=1001.2014.3001.5503

创建yolo-nas的运行环境

进入Pycharm的terminal,输入如下命令

conda create -n yolonas python=3.8

pip install super-gradients

使用自定义数据训练Yolo-nas

准备数据

在YOLO-NAS根目录下创建mydata文件夹(名字可以自定义),目录结构如下:
在这里插入图片描述
将自己数据集里用labelImg标注好的xml文件放到xml目录
图片放到images目录

划分数据集

把划分数据集代码 split_train_val.py放到yolo-nas目录下:

# coding:utf-8

import os
import random
import argparse

# 通过argparse模块创建一个参数解析器。该参数解析器可以接收用户输入的命令行参数,用于指定xml文件的路径和输出txt文件的路径。
parser = argparse.ArgumentParser()
# 指定xml文件的路径
parser.add_argument('--xml_path', default='mydata/xml', type=str, help='input xml label path')
# 设置输出txt文件的路径
parser.add_argument('--txt_path', default='mydata/dataSet', type=str, help='output txt label path')
opt = parser.parse_args()
# 训练集与验证集 占全体数据的比例
trainval_percent = 1.0
# 训练集 占训练集与验证集总体 的比例
train_percent = 0.9
xmlfilepath = opt.xml_path
txtsavepath = opt.txt_path
# 获取到xml文件的数量
total_xml = os.listdir(xmlfilepath)
# 判断txtsavepath是否存在,若不存在,则创建该路径。
if not os.path.exists(txtsavepath):
    os.makedirs(txtsavepath)

# 统计xml文件的个数,即Image标签的个数
num = len(total_xml)
list_index = range(num)
# tv (训练集和测试集的个数) = 数据总数 * 训练集和数据集占全体数据的比例
tv = int(num * trainval_percent)
# 训练集的个数
tr = int(tv * train_percent)
#  按数量随机得到取训练集和测试集的索引
trainval = random.sample(list_index, tv)
#  打乱训练集 
train = random.sample(trainval, tr)
#  创建存放所有图片数据路径的文件
file_trainval = open(txtsavepath + '/trainval.txt', 'w')
#  创建存放所有测试图片数据的路径的文件
file_test = open(txtsavepath + '/test.txt', 'w')
# 创建存放所有训练图片数据的路径的文件
file_train = open(txtsavepath + '/train.txt', 'w')
# 创建存放所有测试图片数据的路径的文件
file_val = open(txtsavepath + '/val.txt', 'w')

# 遍历list_index列表,将文件名按照划分规则写入相应的txt文件中
for i in list_index:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        file_trainval.write(name)
        if i in train:
            file_train.write(name)
        else:
            file_val.write(name)
    else:
        file_test.write(name)

file_trainval.close()
file_train.close()
file_val.close()
file_test.close()

运行代码:
dataSet中出现四个文件,里面是图片的名字
在这里插入图片描述
在这里插入图片描述

根据xml标注文件制作适合yolo的标签

即将每个xml标注提取bbox信息为txt格式,每个图像对应一个txt文件,文件每一行为一个目标的信息,包括class, x_center, y_center, width, height。
创建make_labes.py,复制如下代码运行:

# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
from os import getcwd
 
sets = ['train', 'val', 'test']
classes = ['smoke']   # 改成自己的类别
abs_path = os.getcwd()
print(abs_path)
 
def convert(size, box):
    dw = 1. / (size[0])
    dh = 1. / (size[1])
    x = (box[0] + box[1]) / 2.0 - 1
    y = (box[2] + box[3]) / 2.0 - 1
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x * dw
    w = w * dw
    y = y * dh
    h = h * dh
    return x, y, w, h
 
def convert_annotation(image_id):
    in_file = open('mydata/xml/%s.xml' % (image_id), encoding='UTF-8')
    out_file = open('mydata/label/%s.txt' % (image_id), 'w')
    tree = ET.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)
    for obj in root.iter('object'):
        difficult = obj.find('difficult').text
        cls = obj.find('name').text
        if cls not in classes or int(difficult) == 1:
            continue
        cls_id = classes.index(cls)
        xmlbox = obj.find('bndbox')
        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
             float(xmlbox.find('ymax').text))
        b1, b2, b3, b4 = b
        # 标注越界修正
        if b2 > w:
            b2 = w
        if b4 > h:
            b4 = h
        b = (b1, b2, b3, b4)
        bb = convert((w, h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
 
wd = getcwd()
for image_set in sets:
    if not os.path.exists('mydata/label/'):
        os.makedirs('mydata/label/')
    image_ids = open('mydata/dataSet/%s.txt' % (image_set)).read().strip().split()
    list_file = open('mydata/%s.txt' % (image_set), 'w')
    for image_id in image_ids:
        list_file.write(abs_path + '/mydata/images/%s.jpg\n' % (image_id))
        convert_annotation(image_id)
    list_file.close()

运行完成:
在这里插入图片描述

label目录下出现了图片对应的标记位置(好像是标记框的左上角和由上角的坐标)与类别
在这里插入图片描述

mydata目录下,出现了训练集train.txt,测试集test.txt,里面是对应的图片路径

在这里插入图片描述

将划分好的数据集转成适合yolo-nas要求的数据集

创建data目录

在这里插入图片描述
error目录: 存放格式有问题的图片,格式有问题的图片会中断训练
images/train目录:存放训练集图片
images/val目录:存放测试集图片
labels/train目录:存放训练集图片的标签
labels/val目录:存放测试集图片的标签

根据 trian.txt和val.txt中的图片路径移动图片到train和val文件中

项目目录下创建 mv_img.py,运行代码移动图片

import shutil

# 读取txt文件根据里面的图片路径移动图片到新目录
def move_img(img_txt_path,new_img_path):
    with open(img_txt_path, 'r') as file:
        image_paths = file.readlines()

    # image_new_paths = []
    # for path in image_paths:
    #     path = path.strip()
    #     path = 'mydata/label/'+path+'.txt'
    #     image_new_paths.append(path)

    # 去除路径中的换行符
    image_paths = [path.strip() for path in image_paths]

    # 复制图片到新的文件夹
    # for path in image_new_paths:
    for path in image_paths:
        shutil.copy(path, new_img_path)
img_txt_paths =['mydata/val.txt','mydata/train.txt']
new_img_paths =['data/images/val','data/images/train']

for img_txt_path,new_img_path in zip(img_txt_paths,new_img_paths):
    print(f'img_txt_path,new_img_path = {img_txt_path,new_img_path}')
    move_img(img_txt_path,new_img_path)
# move_img('mydata/train.txt','data/images/train')

项目目录下创建 mv_labels.py,运行代码移动标签

import shutil
# 根据图片名,读取标签的txt文件,并移动到data中label文件夹中
def move_label(img_txt_path,new_label_path):
    with open(img_txt_path, 'r') as file:
        image_paths = file.readlines()

    image_new_paths = []
    for path in image_paths:
        path = path.strip()
        path = 'mydata/label/'+path+'.txt'
        image_new_paths.append(path)
    print(f'image_new_paths = {image_new_paths}')

    # 去除路径中的换行符
    # image_paths = [path.strip() for path in image_paths]

    # 复制图片到新的文件夹
    # for path in image_new_paths:
    for path in image_new_paths:
        print(f'path={path}')
        print(f'new_label_path={new_label_path}')
        shutil.copy(path, new_label_path)

'''移动标签'''
img_name_paths =['mydata/dataSet/val.txt','mydata/dataSet/train.txt']
new_label_paths =['data/labels/val','data/labels/train']

for img_name_path,new_label_path in zip(img_name_paths,new_label_paths):
    print(f'img_name_path,new_img_path = {img_name_path,new_label_path}')
    move_label(img_name_path,new_label_path)

去除训练集测试集中格式错误的代码

在项目的根目录下创建 检查错误jpg.py,复制如下代码运行,这样会去除掉data/images/val/路径下格式错误的图片(错误图片会使训练中的程序中断运行)

import os

train_dir = 'data/images/val/'

def progress(percent, width=50):
    '''进度打印功能'''
    if percent >= 100:
        percent = 100

    show_str = ('[%%-%ds]' % width) % (int(width * percent / 100) * "#")  # 字符串拼接的嵌套使用
    print('\r%s %d%% ' % (show_str, percent), end='')


def is_valid_jpg(jpg_file):
    with open(jpg_file, 'rb') as f:
        f.seek(-2, 2)
        buf = f.read()
        f.close()
        return buf == b'\xff\xd9'  # 判定jpg是否包含结束字段


data_size = len([lists for lists in os.listdir(train_dir) if os.path.isfile(os.path.join(train_dir, lists))])
recv_size = 0
incompleteFile = 0
print('file tall : %d' % data_size)

for file in os.listdir(train_dir):
    if os.path.splitext(file)[1].lower() == '.jpg':
        ret = is_valid_jpg(train_dir + file)
        if ret == False:
            incompleteFile = incompleteFile + 1
            os.remove(train_dir + file)

    recv_per = int(100 * recv_size / data_size)
    progress(recv_per, width=30)
    recv_size = recv_size + 1

progress(100, width=30)
print('\nincomplete file : %d' % incompleteFile)

然后将train_dir = 'data/images/val/'改为train_dir = ‘data/images/train/’,再运行一下代码,去除掉train文件下格式错误的图片

训练代码

import os

import requests
import torch
from PIL import Image

from super_gradients.training import Trainer, dataloaders, models
from super_gradients.training.dataloaders.dataloaders import (
    coco_detection_yolo_format_train, coco_detection_yolo_format_val
)
from super_gradients.training.losses import PPYoloELoss
from super_gradients.training.metrics import DetectionMetrics_050
from super_gradients.training.models.detection_models.pp_yolo_e import (
    PPYoloEPostPredictionCallback
)
class config:
    # trainer params
    CHECKPOINT_DIR = 'checkpoints'  # specify the path you want to save checkpoints to
    EXPERIMENT_NAME = 'cars-from-above'  # specify the experiment name

    # dataset params
    DATA_DIR = 'data'  # parent directory to where data lives

    TRAIN_IMAGES_DIR = 'images/train'  # child dir of DATA_DIR where train images are
    TRAIN_LABELS_DIR = 'labels/train'  # child dir of DATA_DIR where train labels are

    VAL_IMAGES_DIR = 'images/val'  # child dir of DATA_DIR where validation images are
    VAL_LABELS_DIR = 'labels/val'  # child dir of DATA_DIR where validation labels are

    # TEST_IMAGES_DIR = 'images/test'  # child dir of DATA_DIR where validation images are
    # TEST_LABELS_DIR = 'labels/test'  # child dir of DATA_DIR where validation labels are
    CLASSES = ['smoke']  # 指定类名

    NUM_CLASSES = len(CLASSES) # 获取类个数

    # dataloader params - you can add whatever PyTorch dataloader params you have
    # could be different across train, val, and test
    DATALOADER_PARAMS = {
        'batch_size': 16,
        'num_workers': 2
    }

    # model params
    MODEL_NAME = 'yolo_nas_l'  # 可以选择 yolo_nas_s, yolo_nas_m, yolo_nas_l。分别是 小型,中型,大型
    PRETRAINED_WEIGHTS = 'coco'  # only one option here: coco
trainer = Trainer(experiment_name=config.EXPERIMENT_NAME, ckpt_root_dir=config.CHECKPOINT_DIR)

# 指定训练数据
train_data = coco_detection_yolo_format_train(
    dataset_params={
        'data_dir': config.DATA_DIR,
        'images_dir': config.TRAIN_IMAGES_DIR,
        'labels_dir': config.TRAIN_LABELS_DIR,
        'classes': config.CLASSES
    },
    dataloader_params=config.DATALOADER_PARAMS
)

# 指定评估数据
val_data = coco_detection_yolo_format_val(
    dataset_params={
        'data_dir': config.DATA_DIR,
        'images_dir': config.VAL_IMAGES_DIR,
        'labels_dir': config.VAL_LABELS_DIR,
        'classes': config.CLASSES
    },
    dataloader_params=config.DATALOADER_PARAMS
)

# test_data = coco_detection_yolo_format_val(
#     dataset_params={
#         'data_dir': config.DATA_DIR,
#         'images_dir': config.TEST_IMAGES_DIR,
#         'labels_dir': config.TEST_LABELS_DIR,
#         'classes': config.CLASSES
#     },
#     
dataloader_params=config.DATALOADER_PARAMS
# )
# train_data.dataset.plot()

model = models.get(config.MODEL_NAME,
                   num_classes=config.NUM_CLASSES,
                   pretrained_weights=config.PRETRAINED_WEIGHTS
                   )
train_params = {
    # ENABLING SILENT MODE
    "average_best_models":True,
    "warmup_mode": "linear_epoch_step",
    "warmup_initial_lr": 1e-6,
    "lr_warmup_epochs": 3,
    "initial_lr": 5e-4,
    "lr_mode": "cosine",
    "cosine_final_lr_ratio": 0.1,
    "optimizer": "Adam",
    "optimizer_params": {"weight_decay": 0.0001},
    "zero_weight_decay_on_bias_and_bn": True,
    "ema": True,
    "ema_params": {"decay": 0.9, "decay_type": "threshold"},
    # ONLY TRAINING FOR 10 EPOCHS FOR THIS EXAMPLE NOTEBOOK
    "max_epochs": 200,
    "mixed_precision": True,
    "loss": PPYoloELoss(
        use_static_assigner=False,
        # NOTE: num_classes needs to be defined here
        num_classes=config.NUM_CLASSES,
        reg_max=16
    ),
    "valid_metrics_list": [
        DetectionMetrics_050(
            score_thres=0.1,
            top_k_predictions=300,
            # NOTE: num_classes needs to be defined here
            num_cls=config.NUM_CLASSES,
            normalize_targets=True,
            post_prediction_callback=PPYoloEPostPredictionCallback(
                score_threshold=0.01,
                nms_top_k=1000,
                max_predictions=300,
                nms_threshold=0.7
            )
        )
    ],
    "metric_to_watch": 'mAP@0.50'
}

trainer.train(model=model,
              training_params=train_params,
              train_loader=train_data,
              valid_loader=val_data)

best_model = models.get(config.MODEL_NAME,
                        num_classes=config.NUM_CLASSES,
                        checkpoint_path=os.path.join(config.CHECKPOINT_DIR, config.EXPERIMENT_NAME, 'average_model.pth'))

训练时仍然代码检测不到的错误格式图片

在这里插入图片描述根据第一行红字我们发现它的id是338。

根据id找到图片路径

import os


def print_file_name(directory, file_index):
    files = os.listdir(directory)

    # 确保文件索引在有效范围内
    if file_index >= 0 and file_index < len(files):
        file_name = files[file_index]
        print("第{}个文件的文件名为:{}".format(file_index + 1, file_name))
    else:
        print("无法找到第{}个文件".format(file_index + 1))


# 指定目录路径
directory_path = "data/images/train"

# 要打印的文件索引
file_index = 338

# 调用函数打印指定文件的文件名
print_file_name(directory_path, file_index)

发现是:data/images/train/smoke_b002192.jpg

根据文件路径将图片移动到error文件夹

import os
import shutil

def move_file_to_new_directory(file_name):
    # 源目录的路径
    train_dir = "data/images/train"
    # 目标目录的路径
    new_dir = "data/error"

    # 构造文件的完整路径
    file_path = os.path.join(train_dir, file_name)

    if os.path.exists(file_path):
        # 移动文件到new目录
        shutil.move(file_path, new_dir)
        print(f"成功将文件{file_name}移动到new目录")
    else:
        print(f"找不到文件{file_name}")

# 测试 ,输入图片名,将图片移动
move_file_to_new_directory("smoke_b002192.jpg")

再次运行训练代码

成功开始训练。
在这里插入图片描述

连接网络摄像头用训练好的模型参数进行预测

import torch
from super_gradients.training import models
import cv2
import time
def get_video_capture(video, width=None, height=None, fps=None):
    """
     获得视频读取对象
     --   7W   Pix--> width=320,height=240
     --   30W  Pix--> width=640,height=480
     720P,100W Pix--> width=1280,height=720
     960P,130W Pix--> width=1280,height=1024
    1080P,200W Pix--> width=1920,height=1080
    :param video: video file or Camera ID
    :param width:   图像分辨率width
    :param height:  图像分辨率height
    :param fps:  设置视频播放帧率
    :return:
    """
    video_cap = cv2.VideoCapture(video)
    # 如果指定了宽度,高度,fps,则按照制定的值来设置,此处并没有指定
    if width:
        video_cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
    if height:
        video_cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
    if fps:
        video_cap.set(cv2.CAP_PROP_FPS, fps)
    return video_cap

# 此处连接网络摄像头进行测试
video_file = 'rtsp://账号:密码@ip/Streaming/Channels/1'
# video_file = 'data/output.mp4'
num_classes = 1
# best_pth = '/home/computer_vision/code/my_code/checkpoints/cars-from-above/ckpt_best.pth'
best_pth = 'checkpoints/cars-from-above/smoke_small_ckpt_best.pth'
device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")
best_model = models.get("yolo_nas_s", num_classes=num_classes, checkpoint_path=best_pth).to(device)

'''开始计时'''
start_time = time.time()
video_cap = get_video_capture(video_file)
while True:
    isSuccess, frame = video_cap.read()
    if not isSuccess:
        break
    result_image = best_model.predict(frame, conf=0.45, fuse_model=False)
    result_image = result_image._images_prediction_lst[0]
    result_image = result_image.draw()
    '''改动'''
    result_image = cv2.resize(result_image, (960, 540))
    '''end'''
    cv2.namedWindow('result', flags=cv2.WINDOW_NORMAL)
    cv2.imshow('result', result_image)
    kk = cv2.waitKey(1)
    if kk == ord('q'):
        break
video_cap.release()
'''时间结束'''
end_time = time.time()
run_time = end_time - start_time
print(run_time)

补充

对视频进行预测

import torch
from super_gradients.training import models

device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")
model = models.get("yolo_nas_l", pretrained_weights="coco").to(device)
model.predict("data/output.mp4",conf=0.4).save("output/output_lianzhang.mp4")

对图片进行预测

import torch
from super_gradients.training import models

device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")
model = models.get("yolo_nas_s", pretrained_weights="coco").to(device)
out = model.predict("camera01.png", conf=0.6)
out.show()
out.save("output")

预测data目录下的视频并保存预测结果

model.predict("data/output.mp4").save("output/output_lianzhang.mp4")

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

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

相关文章

微信小程序隐私协议模板

在 设置 中找到 用户隐私保护 进行更新&#xff0c;如下图&#xff1a; 具体协议补充可参考如下&#xff1a; 为了分辨用户&#xff0c;开发者将在获取你的明示同意后&#xff0c;收集你的微信昵称、头像 为了显示距离&#xff0c;开发者将在获取你的明示同意后&#xff0c;收…

E7—使用IBERT IP对QSFP+通信链路眼图测试2023-08-11

1.场景 通常在使用光纤接GT收发器进行通信之前&#xff0c;要测试信号质量以确认硬件链路工作正常&#xff0c;xilinx提供了IBERT&#xff08;Integrated Bit Error Ratio Tester&#xff09;进行高速串行通信接口的测试和调试&#xff0c;以KU系列QSFP光纤收发器4路GTY为例介绍…

一文详解Git

一. Git概述 1.1 什么是 Git Git 是一个免费的、开源的分布式版本控制工具&#xff0c; 主要用于管理开发过程中的源代码文件&#xff0c;在软件开发过程中被广泛使用。通过Git仓库来存储和管理这些文件&#xff0c;Git仓库分为二种&#xff1a; 本地仓库&#xff1a;开发人…

命令执行漏洞

1、命令执行漏洞 1.1、简介 Django是用Python开发的一个免费开源的Web结构&#xff0c;几乎包括了Web使用方方面面&#xff0c;能够用于快速建立高性能、文雅的网站&#xff0c;Diango提供了许多网站后台开发常常用到的模块&#xff0c;使开发者可以专注于业务部分。 1.2、漏…

【博学谷学习记录】超强总结,用心分享 | 产品经理之AAARR模型和RFM模型

&#x1f525;前言 本章重点介绍电商行业常用的AARRR模型和RFM模型&#xff0c;并探讨两个模型的实操和适用范围。 &#x1f4c3;目录 &#xff08;一&#xff09;AARRR模型 1.拉新 2.转化 3.留存 4.活跃 5.传播 &#xff08;二&#xff09;RFM模型 1.三个指标的含义 2.RFM模…

【C++】C++异常

文章目录 1. C语言传统处理错误的方式2. C异常的概念3. 异常的使用3.1 异常的抛出和捕获3.2 异常的重新抛出3.3 异常安全3.4 异常规范 4. C标准库的异常体系5. 自定义的异常体系6. 异常的优缺点 1. C语言传统处理错误的方式 C语言传统的错误处理机制有两个&#xff1a; 终止程…

IoTDB 小白“踩坑”心得:入门安装部署篇

小伙伴介绍&#xff01; 大家好&#xff0c;我是 zai&#xff0c;一个基本功不那么扎实、没有太多经验的大学生。我刚刚加入社区&#xff0c;接触 IoTDB&#xff0c;目前仍处于学习阶段&#xff0c;所以我会跟大家分享我学习过程中踩过的一些雷&#xff0c;以及对应的解决办法&…

【百度翻译api】中文自动翻译为英文

欸&#xff0c;最近想做一些nlp的项目&#xff0c;做完了中文的想做做英文的&#xff0c;但是呢&#xff0c;国内爬虫爬取的肯定都是中文 &#xff0c;爬取外网的技术我没有尝试过&#xff0c;没有把握。所以我决定启用翻译&#xff0c;在这期间chatGPT给了我非常多的方法&…

文件上传以及yml的配置

目录 一、存储本地 二、存储到阿里云 三、配置文件信息 一、存储本地 MultipartFile 常见方法&#xff1a; String getOriginalFilename(); //获取原始文件名 void transferTo(File dest); //将接收的文件转存到磁盘文件中 long getSize(); //获取文件的大小&#xff0c;单…

Vue2-绑定样式、条件渲染、列表渲染、列表过滤、模糊查询、Vue监测数据原理

&#x1f954;&#xff1a;想只有苦难&#xff0c;做才有答案 更多Vue知识请点击——Vue.js VUE2-Day3 绑定样式1、class绑定2、绑定style样式 条件渲染1、v-show2、v-if条件渲染案例 列表渲染1、v-for2、key的作用与原理&#xff08;重要&#xff09;面试题&#xff1a;react、…

Android 高手进阶教程(二)之----Android 数据库SQLiteDatabase的使用!!

直接进入主题~ Android 提供了三种数据存储方式&#xff0c;第一种是文件存储;第二种是SharedPreferences 存储;第三种就是数据库SQLiteDatabase 存储。 文件存储我就不用多说了&#xff0c;而SharedPreferences 可以存取简单的数据(int,double,float.etc)&#xff0c;它经常…

数字孪生有哪些应用场景?

数字孪生技术正在越来越普遍。根据艾瑞咨询2023年调查&#xff0c;2022年中国数字孪生市场规模为104亿&#xff0c;同比增长35.0%。随着各行业数字化转型的推进&#xff0c;国内未来数字孪生市场规模将继续增长&#xff0c;预计2023年国内市场规模将达到375亿元。 数字孪生是指…

图片预览插件vue-photo-preview的使用

移动端项目中需要图片预览的功能&#xff0c;但本身使用mintui&#xff0c;vantui中虽然也有&#xff0c;但是为了一个组件安装这个有点儿多余&#xff0c;就选用了vue-photo-preview插件实现&#xff08;其实偷懒也不想自己写&#xff09;。 1、安装 npm i vue-photo-preview…

学以致用:python面向对象和PyEcharts的完美混合技

文章目录 学习目标数据案例分析数据内容需求分析参考代码data_define.pyfile_define.pymain.py 学习目标 使用面向对象思想完成数据读取和处理基于面向对象思想重新认知第三方库使用(PyEcharts) 数据案例分析 数据内容 1月份数据是普通文本&#xff0c;使用逗号分割数据记录&…

生态系统服务(InVEST模型)

第一天&#xff1a; 1. 生态系统服务理论联系实践案例讲解 2. InVEST模型的开发历程、不同版本的差异及对数据需求的讲解 3. InVEST所需数据的要求&#xff08;分辨率、格式、投影系统等&#xff09;、获取及标准化预处理讲解 4. InVEST运行常见问题及处理解决方法讲解 5.…

基于 CentOS 7 构建 LVS-DR 群集 配置nginx负载均衡

环境配置&#xff1a; RHCE客户机192.168.100.146node1lvs192.168.100.145node2RS192.168.100.147node3RS192.168.100.148 配置ipvsadm httpd&#xff1a; [rootnode1 ~]# yum install ipvsadm.x86_64 [rootnode2 ~]# yum install http -y [rootnode2 ~]# systemctl …

Ubuntu18.04版本安装ROS及出现错误的处理方法

前面的文章是在已安装的ROS基础上做的一些应用&#xff0c;这里我们从零开始安装ROS机器人操作系统。 机器人操作系统(Robot Operating System,ROS)是一个开发机器人软件的框架&#xff0c;里面包含了一系列的工具&#xff0c;库和惯例&#xff0c;目的在于简化在大量不同种类机…

从一道面试题来学习前台进程和后台进程、孤儿进程和僵尸进程

1、面试题介绍 以前面试&#xff0c;面试官问了一个问题&#xff0c;大意是&#xff1a; 我们在终端中&#xff0c;通过执行 python main.py 命令&#xff0c;会启动一台前台进程直到程序结束。现在我还是想通过执行 python main.py &#xff0c;启动一个后台进程&#xff0c;…

k8s的yaml文件管理

声明式管理方法&#xff1a; 1.适合于对资源的修改操作2.声明式资源管理方法依赖于资源配置清单文件对资源进行管理资源配置清单文件有两种格式&#xff1a;yaml&#xff08;人性化&#xff0c;易读&#xff09;&#xff0c;json&#xff08;易于api接口解析&#xff09;3.对资…

【实操】2023年npm组件库的创建发布流程

2022年的实践为基础&#xff0c;2023年我再建一个组件库【ZUI】。步骤回顾&#xff1a; 2022年的npm组件包的发布删除教程_npm i ant-design/pro-components 怎么删除_啥咕啦呛的博客-CSDN博客 1.在gitee上创建一个项目,相信你是会的 2.创建初始化项目&#xff0c;看吧&#…