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

VOC数据增强与调整大小

数据增强是针对数据集图像数量太少所采取的一种方法。
博主在实验过程中,使用自己的数据集时发现其数据量过少,只有280张,因此便想到使用数据增强的方式来获取更多的图像信息。对于图像数据,我们可以采用旋转等操作来获取更多的图像。
对于已经标注好的图片,则可以通过imgaug实现对图片和标签中的boundingbox同时变换。
首先,安装依赖库。我们首先创建conda环境,然后下载对应的工具包,这里需要提示一下,里面有个工具包imgaug只能在python2.7,3.4以及3.6使用,并且其需要很多依赖包,我们在直接执行安装imgaug命令时会默认安装opencv-python,但博主却总是安装失败,后来发现可以安装opencv来代替。即:

conda install opencv

考虑到下载速度,可以使用以下命令按照imgaug依赖包:

pip install six numpy scipy matplotlib scikit-image opencv-python imageio tqdm -i https://pypi.tuna.tsinghua.edu.cn/simple

随后也可以直接安装imgaug

conda  install imgaug

接下来是相关功能介绍。

读取原影像bounding boxes坐标

读取xml文件并使用ElementTree对xml文件进行解析,找到每个object的坐标值。

def change_xml_list_annotation(root, image_id, new_target, saveroot, id):in_file = open(os.path.join(root, str(image_id) + '.xml'))  # 这里root分别由两个意思tree = ET.parse(in_file)#修改增强后的xml文件中的filenameelem = tree.find('filename')elem.text = (str(id) + '.jpg')xmlroot = tree.getroot()#修改增强后的xml文件中的pathelem = tree.find('path')if elem != None:elem.text = (saveroot + str(id) + '.jpg')index = 0for object in xmlroot.findall('object'):  # 找到root节点下的所有country节点bndbox = object.find('bndbox')  # 子节点下节点rank的值# xmin = int(bndbox.find('xmin').text)# xmax = int(bndbox.find('xmax').text)# ymin = int(bndbox.find('ymin').text)# ymax = int(bndbox.find('ymax').text)new_xmin = new_target[index][0]new_ymin = new_target[index][1]new_xmax = new_target[index][2]new_ymax = new_target[index][3]xmin = bndbox.find('xmin')xmin.text = str(new_xmin)ymin = bndbox.find('ymin')ymin.text = str(new_ymin)xmax = bndbox.find('xmax')xmax.text = str(new_xmax)ymax = bndbox.find('ymax')ymax.text = str(new_ymax)index = index + 1tree.write(os.path.join(saveroot, str(id + '.xml')))

生成变换序列

产生一个处理图片的Sequential。

# 影像增强seq = iaa.Sequential([iaa.Invert(0.5),iaa.Fliplr(0.5),  # 镜像iaa.Multiply((1.2, 1.5)),  # change brightness, doesn't affect BBsiaa.GaussianBlur(sigma=(0, 3.0)),  # iaa.GaussianBlur(0.5),iaa.Affine(translate_px={"x": 15, "y": 15},scale=(0.8, 0.95),)  # translate by 40/60px on x/y axis, and scale to 50-70%, affects BBs])

bounding box 变化后坐标计算
先读取该影像对应xml文件,获取所有目标的bounding boxes,然后依次计算每个box变化后的坐标。

seq_det = seq.to_deterministic()  # 保持坐标和图像同步改变,而不是随机
# 读取图片
img = Image.open(os.path.join(IMG_DIR, name[:-4] + '.jpg'))
# sp = img.size
img = np.asarray(img)
# bndbox 坐标增强
for i in range(len(bndbox)):bbs = ia.BoundingBoxesOnImage([ia.BoundingBox(x1=bndbox[i][0], y1=bndbox[i][1], x2=bndbox[i][2], y2=bndbox[i][3]),], shape=img.shape)bbs_aug = seq_det.augment_bounding_boxes([bbs])[0]boxes_img_aug_list.append(bbs_aug)# new_bndbox_list:[[x1,y1,x2,y2],...[],[]]n_x1 = int(max(1, min(img.shape[1], bbs_aug.bounding_boxes[0].x1)))n_y1 = int(max(1, min(img.shape[0], bbs_aug.bounding_boxes[0].y1)))n_x2 = int(max(1, min(img.shape[1], bbs_aug.bounding_boxes[0].x2)))n_y2 = int(max(1, min(img.shape[0], bbs_aug.bounding_boxes[0].y2)))if n_x1 == 1 and n_x1 == n_x2:n_x2 += 1if n_y1 == 1 and n_y2 == n_y1:n_y2 += 1if n_x1 >= n_x2 or n_y1 >= n_y2:print('error', name)new_bndbox_list.append([n_x1, n_y1, n_x2, n_y2])
# 存储变化后的图片
image_aug = seq_det.augment_images([img])[0]
path = os.path.join(AUG_IMG_DIR,str(str(name[:-4]) + '_' + str(epoch)) + '.jpg')
image_auged = bbs.draw_on_image(image_aug, thickness=0)
Image.fromarray(image_auged).save(path)# 存储变化后的XML
change_xml_list_annotation(XML_DIR, name[:-4], new_bndbox_list, AUG_XML_DIR,str(name[:-4]) + '_' + str(epoch))
# print(str(str(name[:-4]) + '_' + str(epoch)) + '.jpg')
new_bndbox_list = []

开始增强

数据准备
输入数据为两个文件夹一个是需要增强的图像数据(JPEGImages),一个是对应的xml文件(Annotations)。注意:图像文件名需和xml文件名相对应!

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

之后执行即可,得到文件如下:

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

完整代码

import xml.etree.ElementTree as ET
import os
import imgaug as ia
import numpy as np
import shutil
from tqdm import tqdm
from PIL import Image
from imgaug import augmenters as iaaia.seed(1)def read_xml_annotation(root, image_id):in_file = open(os.path.join(root, image_id))tree = ET.parse(in_file)root = tree.getroot()bndboxlist = []for object in root.findall('object'):  # 找到root节点下的所有country节点bndbox = object.find('bndbox')  # 子节点下节点rank的值xmin = int(bndbox.find('xmin').text)xmax = int(bndbox.find('xmax').text)ymin = int(bndbox.find('ymin').text)ymax = int(bndbox.find('ymax').text)# print(xmin,ymin,xmax,ymax)bndboxlist.append([xmin, ymin, xmax, ymax])# print(bndboxlist)bndbox = root.find('object').find('bndbox')return bndboxlistdef change_xml_list_annotation(root, image_id, new_target, saveroot, id):in_file = open(os.path.join(root, str(image_id) + '.xml'))  # 这里root分别由两个意思tree = ET.parse(in_file)# 修改增强后的xml文件中的filenameelem = tree.find('filename')elem.text = (str(id) + '.jpg')xmlroot = tree.getroot()# 修改增强后的xml文件中的pathelem = tree.find('path')if elem != None:elem.text = (saveroot + str(id) + '.jpg')index = 0for object in xmlroot.findall('object'):  # 找到root节点下的所有country节点bndbox = object.find('bndbox')  # 子节点下节点rank的值# xmin = int(bndbox.find('xmin').text)# xmax = int(bndbox.find('xmax').text)# ymin = int(bndbox.find('ymin').text)# ymax = int(bndbox.find('ymax').text)new_xmin = new_target[index][0]new_ymin = new_target[index][1]new_xmax = new_target[index][2]new_ymax = new_target[index][3]xmin = bndbox.find('xmin')xmin.text = str(new_xmin)ymin = bndbox.find('ymin')ymin.text = str(new_ymin)xmax = bndbox.find('xmax')xmax.text = str(new_xmax)ymax = bndbox.find('ymax')ymax.text = str(new_ymax)index = index + 1tree.write(os.path.join(saveroot, str(id + '.xml')))def mkdir(path):# 去除首位空格path = path.strip()# 去除尾部 \ 符号path = path.rstrip("\\")# 判断路径是否存在# 存在     True# 不存在   FalseisExists = os.path.exists(path)# 判断结果if not isExists:# 如果不存在则创建目录# 创建目录操作函数os.makedirs(path)print(path + ' 创建成功')return Trueelse:# 如果目录存在则不创建,并提示目录已存在print(path + ' 目录已存在')return Falseif __name__ == "__main__":IMG_DIR = "E:/KITTI/jiamusi/VOC/JPEGImage/"#图像原路径XML_DIR = "E:/KITTI/jiamusi/VOC/VOC_Annotation/"AUG_XML_DIR = "E:/KITTI/jiamusi/VOC/VOC/Annotations/"  # 存储增强后的XML文件夹路径try:shutil.rmtree(AUG_XML_DIR)except FileNotFoundError as e:a = 1mkdir(AUG_XML_DIR)AUG_IMG_DIR = "E:/KITTI/jiamusi/VOC/VOC/JPEGImages/"  # 存储增强后的影像文件夹路径try:shutil.rmtree(AUG_IMG_DIR)except FileNotFoundError as e:a = 1mkdir(AUG_IMG_DIR)AUGLOOP = 5  # 每张影像增强的数量,博主有近260张,增强5次即可boxes_img_aug_list = []new_bndbox = []new_bndbox_list = []# 影像增强seq = iaa.Sequential([iaa.Invert(0.5),iaa.Fliplr(0.5),  # 镜像iaa.Multiply((1.2, 1.5)),  # change brightness, doesn't affect BBsiaa.GaussianBlur(sigma=(0, 3.0)),  # iaa.GaussianBlur(0.5),iaa.Affine(translate_px={"x": 15, "y": 15},scale=(0.8, 0.95),)  # translate by 40/60px on x/y axis, and scale to 50-70%, affects BBs])for name in tqdm(os.listdir(XML_DIR), desc='Processing'):bndbox = read_xml_annotation(XML_DIR, name)# 保存原xml文件shutil.copy(os.path.join(XML_DIR, name), AUG_XML_DIR)# 保存原图og_img = Image.open(IMG_DIR + '/' + name[:-4] + '.jpg')og_img.convert('RGB').save(AUG_IMG_DIR + name[:-4] + '.jpg', 'JPEG')og_xml = open(os.path.join(XML_DIR, name))tree = ET.parse(og_xml)# 修改增强后的xml文件中的filenameelem = tree.find('filename')elem.text = (name[:-4] + '.jpg')tree.write(os.path.join(AUG_XML_DIR, name))for epoch in range(AUGLOOP):seq_det = seq.to_deterministic()  # 保持坐标和图像同步改变,而不是随机# 读取图片img = Image.open(os.path.join(IMG_DIR, name[:-4] + '.jpg'))# sp = img.sizeimg = np.asarray(img)# bndbox 坐标增强for i in range(len(bndbox)):bbs = ia.BoundingBoxesOnImage([ia.BoundingBox(x1=bndbox[i][0], y1=bndbox[i][1], x2=bndbox[i][2], y2=bndbox[i][3]),], shape=img.shape)bbs_aug = seq_det.augment_bounding_boxes([bbs])[0]boxes_img_aug_list.append(bbs_aug)# new_bndbox_list:[[x1,y1,x2,y2],...[],[]]n_x1 = int(max(1, min(img.shape[1], bbs_aug.bounding_boxes[0].x1)))n_y1 = int(max(1, min(img.shape[0], bbs_aug.bounding_boxes[0].y1)))n_x2 = int(max(1, min(img.shape[1], bbs_aug.bounding_boxes[0].x2)))n_y2 = int(max(1, min(img.shape[0], bbs_aug.bounding_boxes[0].y2)))if n_x1 == 1 and n_x1 == n_x2:n_x2 += 1if n_y1 == 1 and n_y2 == n_y1:n_y2 += 1if n_x1 >= n_x2 or n_y1 >= n_y2:print('error', name)new_bndbox_list.append([n_x1, n_y1, n_x2, n_y2])# 存储变化后的图片image_aug = seq_det.augment_images([img])[0]path = os.path.join(AUG_IMG_DIR,str(str(name[:-4]) + '_' + str(epoch)) + '.jpg')image_auged = bbs.draw_on_image(image_aug, size=0)Image.fromarray(image_auged).convert('RGB').save(path)# 存储变化后的XMLchange_xml_list_annotation(XML_DIR, name[:-4], new_bndbox_list, AUG_XML_DIR,str(name[:-4]) + '_' + str(epoch))# print(str(str(name[:-4]) + '_' + str(epoch)) + '.jpg')new_bndbox_list = []print('Finish!')

但运行后发现问题,图片太大,所有我们需要进行修改。
原图为6960x46460,其不但图片较大,且6960不能整除32,造成错误。因此需要将图片进行修改。并要在resize的同时还要将标注信息一并转换。

import glob
import xml.dom.minidom
import cv2
from PIL import Image
import matplotlib.pyplot as plt
import os# 定义待批量裁剪图像的路径地址
IMAGE_INPUT_PATH = r'E:\KITTI\jiamusi\VOC\VOC2023\JPEGImages'
XML_INPUT_PATH = r'E:\KITTI\jiamusi\VOC\VOC2023\Annotations'
# 定义裁剪后的图像存放地址
IMAGE_OUTPUT_PATH = r'E:\KITTI\jiamusi\VOC\VOC2023\VOC2023\JPEGImages'
XML_OUTPUT_PATH = r'E:\KITTI\jiamusi\VOC\VOC2023\VOC2023\Annotations'
imglist = os.listdir(IMAGE_INPUT_PATH)
xmllist = os.listdir(XML_INPUT_PATH)for i in range(len(imglist)):# 每个图像全路径image_input_fullname = IMAGE_INPUT_PATH + '/' + imglist[i]xml_input_fullname = XML_INPUT_PATH + '/' + xmllist[i]image_output_fullname = IMAGE_OUTPUT_PATH + '/' + imglist[i]xml_output_fullname = XML_OUTPUT_PATH + '/' + xmllist[i]img = cv2.imread(image_input_fullname)height, width = img.shape[:2]# 定义缩放信息 以等比例缩放到416为例scale1 = 720/ height #yscale2 = 1280 / width#xheight = 720width = 1280dom = xml.dom.minidom.parse(xml_input_fullname)root = dom.documentElement# 读取标注目标框objects = root.getElementsByTagName("bndbox")for object in objects:xmin = object.getElementsByTagName("xmin")xmin_data = int(float(xmin[0].firstChild.data))# xmin[0].firstChild.data =str(int(xmin1 * x))ymin = object.getElementsByTagName("ymin")ymin_data = int(float(ymin[0].firstChild.data))xmax = object.getElementsByTagName("xmax")xmax_data = int(float(xmax[0].firstChild.data))ymax = object.getElementsByTagName("ymax")ymax_data = int(float(ymax[0].firstChild.data))# 更新xmlwidth_xml = root.getElementsByTagName("width")width_xml[0].firstChild.data = widthheight_xml = root.getElementsByTagName("height")height_xml[0].firstChild.data = heightxmin[0].firstChild.data = int(xmin_data * scale2)ymin[0].firstChild.data = int(ymin_data * scale1)xmax[0].firstChild.data = int(xmax_data * scale2)ymax[0].firstChild.data = int(ymax_data * scale1)# 另存更新后的文件with open(xml_output_fullname, 'w') as f:dom.writexml(f, addindent='  ', encoding='utf-8')# 测试缩放效果img = cv2.resize(img, (width, height))'''# xmin, ymin, xmax, ymax分别为xml读取的坐标信息left_top = (int(xmin_data*scale), int(ymin_data*scale))right_down= (int(xmax_data*scale), int(ymax_data*scale))cv2.rectangle(img, left_top, right_down, (255, 0, 0), 1)'''cv2.imwrite(image_output_fullname, img)

将其转换为1280x720的格式,就可以使用了。
在这里插入图片描述

相关文章:

VOC数据增强与调整大小

数据增强是针对数据集图像数量太少所采取的一种方法。 博主在实验过程中,使用自己的数据集时发现其数据量过少,只有280张,因此便想到使用数据增强的方式来获取更多的图像信息。对于图像数据,我们可以采用旋转等操作来获取更多的图…...

Linux 安装jenkins和jdk11

Linux 安装jenkins和jdk111. Install Jdk112. Jenkins Install2.1 Install Jenkins2.2 Start2.3 Error3.Awakening1.1 Big Data -- Postgres4. Awakening1. Install Jdk11 安装jdk11 sudo yum install fontconfig java-11-openjdk 2. Jenkins Install 2.1 Install Jenkins 下…...

Pandas——Series操作【建议收藏】

pandas——Series操作 作者:AOAIYI 创作不易,觉得文章不错或能帮助到你学习,可以点赞收藏评论哦 文章目录pandas——Series操作一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.创建Series2.从具体位置的Series中访问数据3.使…...

JUC并发编程Ⅰ -- Java中的线程

文章目录线程与进程并行与并发进程与线程应用应用之异步调用应用之提高效率线程的创建方法一:通过继承Thread类创建方法二:使用Runnable配合Thread方法三:使用FutureTask与Thread结合创建查看进程和线程的方法线程运行的原理栈与栈帧线程上下…...

基于vue-admin-element开发后台管理系统【技术点整理】

一、Vue点击跳转外部链接 点击重新打开一个页面窗口,不覆盖当前的页面 window.open(https://www.baidu.com,"_blank")"_blank" 新打开一个窗口"_self" 覆盖当前的窗口例如:导入用户模板下载 templateDownload() {wi…...

【C语言学习笔记】:通讯录管理系统

系统中需要实现的功能如下: ✿ 添加联系人:向通讯录中添加新人,信息包括(姓名、性别、年龄、联系电话、家庭住址)最多记录1000人 ✿ 显示联系人:显示通讯录中所有的联系人信息 ✿ 删除联系人:按…...

开关电源环路稳定性分析(10)——OPA和OTA型补偿器传递函数

大家好,这里是大话硬件。 在前面9讲的内容中将开关电源环路分析进行了梳理,我相信很多人即使都看完了,应该还是不会设计,而且还存在几个疑问。比如我随便举几个: 开关电源的带宽怎么设定?开关电源精度和什…...

2.11知识点整理(关于pycharm,python,pytorch,conda)

pycharm 设置anaconda环境: File -> Settings->选择左侧的project xxx再选择打开Project Interpreter页->选择add添加解释器->添加Anaconda中Python解释器(Anaconda安装目录下的python.exe) (选择existing environment &#xff…...

Linux服务器开发-2. Linux多进程开发

文章目录1. 进程概述1.1 程序概览1.2 进程概念1.3 单道、多道程序设计1.4 时间片1.5 并行与并发1.6 进程控制块(PCB)2. 进程的状态转换2.1 进程的状态2.2 进程相关命令查看进程实时显示进程动态杀死进程进程号和相关函数3. 进程的创建-fork函数3.1 进程创…...

Excel中缺失数据值的自动填充

目录简单方法示例1:数据满足线性趋势示例2:数据满足增长(指数)趋势参考实验做完处理数据,发现有一组数据因为设备中途出现问题缺失了,之前做过的数据也找不到,为了不影响后续处理,这里使用Excel插入缺失值。…...

路由器刷固件

前言 我希望可以远程访问我的电脑。但,我不希望电脑总是处于运行状态,因为那样比较费电。所以需要一个方案,能将睡眠/关机中的电脑唤醒。 方案一:选用智能插座,远程给电脑上电。电脑设置上电自启。但,这存…...

leetcode: Two Sum II - Input Array is Sorted

leetcode: Two Sum II - Input Array is Sorted1. 题目2. 解答3. 总结1. 题目 Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such that they add up to a specific target number. Let these two number…...

STL——list

一、list介绍及使用 1. list文档介绍 (1)list是可以在常数范围内,在任意位置进行插入、删除的序列式容器,并且该容器可以前后双向迭代。 (2)list的底层是带头结点的双向循环链表,其中每个元素…...

实战打靶集锦-004-My-Cmsms

**写在前面:**记录一次艰难曲折的打靶经历。 目录1. 主机发现2. 端口扫描3. 服务枚举4. 服务探查4.1 WEB服务探查4.1.1 浏览器访问4.1.2 目录枚举4.1.3 控制台探查4.1.4 其他目录探查4.2 阶段小结5. 公共EXP搜索5.1 CMS搜索5.2 Apache搜索5.3 PHP搜索5.4 MySQL搜索5…...

c++代码实现我的世界(14)

c代码实现我的世界14|生成地貌兼工作台1前言的前言~前言生成地貌函数结构体struct dimao根据比例生成地貌工作台函数准备的东西写在最后前言的前言~ 实在对不起大家,有挺长时间没更新了。 前言 今天我们将写生成地形的函数与工作台前传的代码; 注&…...

RMQ--区间最值问题(在更)

RMQ(Range Minimum/Maximum Query)RMQ解决的问题ST算法 O(nlogn)线段树例题数列区间最大值最敏捷的机器人天才的记忆Frequent values总结(ST和线段树对比)RMQ解决的问题 RMQ是一个解决多个区间最值查询的算法,即区间最值查询&…...

一篇文章搞懂Cookie

目录 1 什么是Cookie 2 创建Cookie 3 浏览器查看Cookie 3.1 浏览器查看Cookie的第一种方式 3.2 浏览器查看Cookie的第二种方式 4 获取Cookie 5 修改Cookie 6 Cookie编码与解码 6.1 创建带中文Cookie 6.2 读取带中文Cookie 6.3 获取中文Cookie请求效果 6.4 解决创建和…...

深入解读.NET MAUI音乐播放器项目(二):播放内核

播放控制服务 IMusicControlService: 播放控制类,用于当前平台播放器对象的操作,对当前所播放曲目的暂停/播放,下一首/上一首,快进快退(寻迹),随机、单曲模式等功能的控制。 播放控制类包含一…...

4.SpringWeb

一、创建项目LomBok:辅助开发工具,减少代码编写Spring Web:带上Spring MVC,可以做Web开发了Thymleaf: Web开发末班引擎(不常用)创建好,如下:static/ 放置静态资源的根目录templates/ 放置模板文件的根目录 二、资源配置…...

C++中的枚举与位域

枚举在传统 C中,枚举类型并非类型安全,枚举类型会被视作整数,则会让两种完全不同的枚举类型可以进行直接的比较(虽然编译器给出了检查,但并非所有),甚至同一个命名空间中的不同枚举类型的枚举值…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...

Spring Boot面试题精选汇总

🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...

6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙

Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...

大模型——基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程

基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程 下载安装Docker Docker官网:https://www.docker.com/ 自定义Docker安装路径 Docker默认安装在C盘,大小大概2.9G,做这行最忌讳的就是安装软件全装C盘,所以我调整了下安装路径。 新建安装目录:E:\MyS…...