从零搭建pytorch模型教程(八)实践部分(二)目标检测数据集格式转换
前言
图像目标检测领域有一个非常著名的数据集叫做COCO,基本上现在在目标检测领域发论文,COCO是不可能绕过的Benchmark。因此许多的开源目标检测算法框架都会支持解析COCO数据集格式。通过将其他数据集格式转换成COCO格式可以无痛的使用这些开源框架来训练新的数据集,比如Pascal VOC数据集。
本文首先将介绍COCO和VOC目标检测数据集格式以及VOC转换到COCO格式的核心步骤,最后将自定义一种数据格式利用上述核心步骤将其转换到COCO格式下。只要理解了不同数据集的标注方法,转换数据集其实就是一个非常简单自然的过程,可以拓展到任意方式标注的数据集上。
数据集格式介绍
COCO
其实COCO数据集的标签内容不仅仅涵盖目标检测,还包含了目标关键点、实例Mask以及图片描述等信息。在这里我们着重介绍COCO的目标检测相关内容。我们以COCO2017为例先看看其标签文件结构:

其中红框框出来的就是以Json格式组织的目标检测相关的标注文件,其主要由三个部分构成:
"info"字段:数据集的基本信息描述、版本号、年份等信息。
“images字段” :包含了图片路径、宽高信息、唯一标志ID等信息。
“annotations字段”:包含了图片中的Box位置、类别等信息。
其简单示例如下所示:
{"info": {"description": "COCO 2017 Dataset","url": "http://cocodataset.org","version": "1.0","year": 2017,"contributor": "COCO Consortium","date_created": "2017/09/01"},
"images": [{"license": 4,"file_name": "000000397133.jpg","coco_url": "http://images.cocodataset.org/val2017/000000397133.jpg","height": 427,"width": 640,"date_captured": "2013-11-14 17:02:52","flickr_url": "","id": 397133},{"license": 1,"file_name": "000000037777.jpg","coco_url": "http://images.cocodataset.org/val2017/000000037777.jpg","height": 230,"width": 352,"date_captured": "2013-11-14 20:55:31","flickr_url": "","id": 37777}],"annotations": [{"area": 702.1057499999998, //Box的尺寸"image_id": 289343, //对应的图像ID"bbox": [473.07, //左上角点x坐标395.93, //左上角点y坐标38.65, //Box的宽28.67 //Box的高],"category_id": 18, //对应的类别"id": 1768, //该标签独有ID"iscrowd": 0 //0表示非密集场景,1表示密集场景}]
}
PASCAL VOC
PASCAL VOC数据集有两个相对重要年份的数据集:PASCAL VOC 2007与PASCAL VOC 2012,每年都会在上一年的基础上增加一些额外的数据或标签。PASCAL VOC数据集也涵盖了分类、检测、分割、动作识别等标签。
我们这里着重介绍其检测部分,以PASCAL VOC 2012数据集为例,包含了20个类别1W+数据集,2W+标注Box的目标。其标签格式是每一个图片都有一个对应的XML文件作为其标注信息载体,标注信息主要包含如下几方面内容:
图像基本信息:图像名、图像尺寸等
object字段:目标分类标签、box标签(xmin,ymin,xmax,ymax)等信息
XML主要格式如下:
<annotation><folder>VOC2012</folder><filename>2007_000063.jpg</filename> //标签对应的图片文件<source><database>The VOC2007 Database</database><annotation>PASCAL VOC2007</annotation><image>flickr</image></source><size> //图像尺寸<width>500</width><height>375</height><depth>3</depth></size><segmented>1</segmented><object><name>dog</name> //类别<pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult> //1表示这个目标是比较难识别的<bndbox> //box信息<xmin>123</xmin> //左上角x坐标<ymin>115</ymin> //左上角y坐标<xmax>379</xmax> //右下角x坐标<ymax>275</ymax> //右下角y坐标</bndbox></object><object><name>chair</name><pose>Frontal</pose><truncated>1</truncated><difficult>0</difficult><bndbox><xmin>75</xmin><ymin>1</ymin><xmax>428</xmax><ymax>375</ymax></bndbox></object>
</annotation>
数据集格式转换
在知道了各个数据集格式的基础上做数据集格式的转换就已经是非常简单的任务了,也有很多优秀的开源框架已经帮我们做好了这些事情比如MMDetection中就已经提供好了现成的工具供我们白嫖(bushi),使用了。
我们抽取其一些核心部分来一起看看,详细代码请参考MMDetection
Github:https://github.com/openmmlab/mmdetection/tree/master/tools/dataset_converters
从不同的数据集转换到COCO下主要也就两个步骤:
解析待转换数据集格式。
用COCO格式重构Json文件。
上述第二步对任意待转换数据集都是一样的,可以抽象为一个函数,输入的是解析好的不同数据集的Box信息等数据。下面我们以几个不同的数据集为例介绍。
From VOC to COCO
从VOC数据集到COCO数据集格式转换主要包含如下两个步骤:
解析VOC数据集数据:遍历图片以及对应XML文件,返回一个数组A,数组中的每一个实例包含了图片路径、Box相关标注信息等。
遍历A将A中的实例信息用COCO的格式表达出来并生成Json文件
其主要由两块核心代码构成,一个是VOC的XML文件解析,一个是Json文件生成。
VOC XML标注文件解析
xml文件解析已经有下面这个非常方便的Python库供大家使用
import xml.etree.ElementTree as ETdef parse_xml(args):xml_path, img_path = argstree = ET.parse(xml_path) # 构建XML文件解析树root = tree.getroot() # 获取XML文件的根节点size = root.find('size') # 获取图像的尺寸w = int(size.find('width').text) # 图像宽高h = int(size.find('height').text)bboxes = []labels = []bboxes_ignore = []labels_ignore = []for obj in root.findall('object'): # 遍历object字段下所有box信息name = obj.find('name').textlabel = label_ids[name]difficult = int(obj.find('difficult').text) #这个difficult对应的是COCO中iscrowdedbnd_box = obj.find('bndbox')bbox = [ # 对应的Box标注信息(x1,y1,x2,y2)int(bnd_box.find('xmin').text),int(bnd_box.find('ymin').text),int(bnd_box.find('xmax').text),int(bnd_box.find('ymax').text)]if difficult: # 将difficult属性的Box放入ignore列表bboxes_ignore.append(bbox) # 最后计算AP时这个GT是被忽略的labels_ignore.append(label)else:bboxes.append(bbox)labels.append(label)if not bboxes:bboxes = np.zeros((0, 4))labels = np.zeros((0, ))else:bboxes = np.array(bboxes, ndmin=2) - 1labels = np.array(labels)if not bboxes_ignore:bboxes_ignore = np.zeros((0, 4))labels_ignore = np.zeros((0, ))else:bboxes_ignore = np.array(bboxes_ignore, ndmin=2) - 1labels_ignore = np.array(labels_ignore)annotation = {'filename': img_path,'width': w,'height': h,'ann': {'bboxes': bboxes.astype(np.float32),'labels': labels.astype(np.int64),'bboxes_ignore': bboxes_ignore.astype(np.float32),'labels_ignore': labels_ignore.astype(np.int64)}}return annotation
用解析好的annotation重构COCO格式的Json文件:
import numpy as npdef voc_classes():return ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat','chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person','pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor']def cvt_to_coco_json(annotations):image_id = 0annotation_id = 0coco = dict()coco['images'] = []coco['type'] = 'instance'coco['categories'] = []coco['annotations'] = []image_set = set()# coco annotations字段添加标注信息def addAnnItem(annotation_id, image_id, category_id, bbox, difficult_flag):annotation_item = dict()annotation_item['segmentation'] = []# 这里省略了seg部分代码seg = []annotation_item['segmentation'].append(seg)# 转换为COCO对应的x1,y1,w,h格式xywh = np.array([bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1]])annotation_item['area'] = int(xywh[2] * xywh[3])# 如果difficult标志为1,该目标对应coco中iscrowd字段为1表明为密集目标场景if difficult_flag == 1:annotation_item['ignore'] = 0annotation_item['iscrowd'] = 1else:annotation_item['ignore'] = 0annotation_item['iscrowd'] = 0annotation_item['image_id'] = int(image_id)annotation_item['bbox'] = xywh.astype(int).tolist()annotation_item['category_id'] = int(category_id)annotation_item['id'] = int(annotation_id)coco['annotations'].append(annotation_item)return annotation_id + 1#for category_id, name in enumerate(voc_classes()):category_item = dict()category_item['supercategory'] = str('none')category_item['id'] = int(category_id)category_item['name'] = str(name)coco['categories'].append(category_item)for ann_dict in annotations:file_name = ann_dict['filename']ann = ann_dict['ann']assert file_name not in image_setimage_item = dict()image_item['id'] = int(image_id)image_item['file_name'] = str(file_name)image_item['height'] = int(ann_dict['height'])image_item['width'] = int(ann_dict['width'])coco['images'].append(image_item) # 设置COCO的"images"字段image_set.add(file_name)# 设置COCO的"annotations"字段bboxes = ann['bboxes'][:, :4] # 获取box和label类别信息labels = ann['labels']for bbox_id in range(len(bboxes)):bbox = bboxes[bbox_id]label = labels[bbox_id]annotation_id = addAnnItem(annotation_id, image_id, label, bbox, difficult_flag=0)# ignore的目标表示该GT被忽视bboxes_ignore = ann['bboxes_ignore'][:, :4]labels_ignore = ann['labels_ignore']for bbox_id in range(len(bboxes_ignore)):bbox = bboxes_ignore[bbox_id]label = labels_ignore[bbox_id]annotation_id = addAnnItem(annotation_id, image_id, label, bbox, difficult_flag=1)image_id += 1return coco
拿到返回的coco对象后只需要调用下列方法就可以将对象序列化成Json文件了。
import mmcv
mmcv.dump(coco, out_file) # out_file为输出的json文件名
值得注意的一点是上面提到的iscorwd这个字段,这个字段标注为1时,最后统计AP时,该GT与预测框完成匹配后还可以考虑与其他预测框进行匹配,允许多个预测框与其匹配(因为场景是密集的)。
自定义格式数据集 to COCO
首先我们自定义一种数据标注格式,我们用txt文件作为标注信息的载体,将txt文件与图像文件通过相同的文件名一一对应。分别将标签文件以及对应图像文件放在Annotations以及JPEGImages文件夹下,同时我们生成JPEGImages图像文件的filelist.txt文件,这个文件每一行对应一个图像文件的全路径:

txt文件格式如下:

第一列表示类别,从0开始;第二到第五列表示Box信息依次为中心点x方向坐标,中心点y方向坐标,box的宽以及高(cx,cy,w,h)。
我们同样使用前面介绍过的cvt_to_coco_json将固定格式的annotations转换为COCO格式,那么我们只需要编写解析自定义格式数据集生成annotations的代码即可:
box尺寸小于min_size的作为ignore对象
file_path为图像路径的filelist.txt文件的全路径
def parse_info(file_path, min_size):annotations = []invalid_img = 0small_box = 0with open(file_path, 'r') as f:for l in tqdm(f):img_file = l.rstrip()img = cv2.imread(img_file)if img is None:invalid_img += 1continueh,w,_ = img.shape# 获取对应的标签文件ann_file = img_file.replace("JPEGImages", "Annotations").replace \(".png", ".txt").replace(".jpg", ".txt")annotation = {'filename' : img_file,'height' : h,'width' : w,'ann' : {}}if not osp.exists(ann_file):annotations.append(annotation)continueboxes, labels = [], []boxes_ignore, labels_ignore = [], []with open(ann_file, 'r') as fr:for anno in fr:anno_list = anno.rstrip().split(' ')cls = int(anno_list[0])cx, cy = float(anno_list[1]), float(anno_list[2])w, h = float(anno_list[3]), float(anno_list[4])# 转换为COCO box表示格式x1 = max(0, int(cx-w/2))y1 = max(0, int(cy-h/2))box = [x1, y1, w, h]if w >= min_size and h >= min_size:labels.append(cls)boxes.append(box)else:labels_ignore.append(cls)boxes_ignore.append(box) boxes = np.zeros((0, 4)) if len(boxes) == 0 else np.array(boxes)labels = np.zeros((0, )) if len(labels) == 0 else np.array(labels) boxes_ignore = np.zeros((0, 4)) if len(boxes_ignore) == 0 else np.array(boxes_ignore)labels_ignore = np.zeros((0, )) if len(labels_ignore) == 0 else np.array(labels_ignore) annotation['ann']['bboxes'] = np.array(boxes)annotation['ann']['labels'] = np.array(labels)annotation['ann']['bboxes_ignore'] = np.array(boxes_ignore)annotation['ann']['labels_ignore'] = np.array(labels_ignore)annotations.append(annotation)print('INFO:Invalid IMG:{}'.format(invalid_img))return annotations
写在后面
数据集的转换是非常有必要的,在软件设计中我们希望一套代码尽可能多的为不同情况服务。在这里我们希望训练代码中一套数据集(Dataset)class代码来完成所有目标检测任务训练,而不是针对不同的数据集设计不同的Dataset class代码。而对于目标检测来说,COCO可能就是这个最佳的模板~
最后
感谢你们的阅读和喜欢,我收藏了很多技术干货,可以共享给喜欢我文章的朋友们,如果你肯花时间沉下心去学习,它们一定能帮到你。
因为这个行业不同于其他行业,知识体系实在是过于庞大,知识更新也非常快。作为一个普通人,无法全部学完,所以我们在提升技术的时候,首先需要明确一个目标,然后制定好完整的计划,同时找到好的学习方法,这样才能更快的提升自己。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

一、全套AGI大模型学习路线
AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

二、640套AI大模型报告合集
这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

三、AI大模型经典PDF籍
随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

四、AI大模型商业化落地方案

五、面试资料
我们学习AI大模型必然是想找到高薪的工作,下面这些面试题都是总结当前最新、最热、最高频的面试题,并且每道题都有详细的答案,面试前刷完这套面试题资料,小小offer,不在话下。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

相关文章:
从零搭建pytorch模型教程(八)实践部分(二)目标检测数据集格式转换
前言 图像目标检测领域有一个非常著名的数据集叫做COCO,基本上现在在目标检测领域发论文,COCO是不可能绕过的Benchmark。因此许多的开源目标检测算法框架都会支持解析COCO数据集格式。通过将其他数据集格式转换成COCO格式可以无痛的使用这些开源框架来训…...
MYSQL(2) 高级查询
文章目录 概述高级查询基础查询条件查询范围查询判空查询模糊查询分页查询查询后排序分组查询 小结 概述 接上篇,上篇写到增删改查。这篇继续。 高级查询 基础查询 -- 全部查询 select * from student; -- 只查询部分字段 select sname, class_id from student;…...
小程序的运营方法:从入门到精通
随着科技的快速发展,小程序已成为我们日常生活和工作中不可或缺的一部分。小程序无需下载安装,即用即走的特点深受用户喜爱。那么,如何运营好一个小程序呢?下面就为大家分享一些小程序的运营方法。 一、明确目标用户 在运营小程序…...
【优秀python算法毕设】基于python时间序列模型分析气温变化趋势的设计与实现
1 绪论 1.1 研究背景与意义 在气候变化日益受到全球关注的背景下,天气气温的变化已经对人们的生活各方面都产生了影响,人们在外出时大多都会在手机上看看天气如何,根据天气的变化来决定衣物的穿着和出行的安排。[1]如今手机能提供的信息已经…...
掌握 Symfony 路由系统:配置与管理
掌握 Symfony 路由系统:配置与管理 Symfony 是一个非常流行的 PHP 框架,而路由系统是 Symfony 框架的核心组件之一。通过理解和掌握 Symfony 的路由系统,开发者可以更高效地配置和管理应用程序的 URL 结构,从而更好地控制应用程序…...
OpenTeleVision复现及机器人迁移
相关信息 标题 Open-TeleVision: Teleoperation with Immersive Active Visual Feedback作者 Xuxin Cheng1 Jialong Li1 Shiqi Yang1 Ge Yang2 Xiaolong Wang1 UC San Diego1 MIT2主页 https://robot-tv.github.io/链接 https://robot-tv.github.io/resources/television.pdf代…...
气膜足球馆:经济高效的室内足球场馆解决方案—轻空间
如果你有一片足球场,想要建一个室内的足球馆,为什么不考虑一下气膜建筑呢?气膜建筑以其独特的优势和高性价比,成为现代体育场馆建设中的一匹黑马。它不仅具有传统建筑无法比拟的经济效益和快速施工优势,还在智能控制、…...
Vue3二次封装axios
官网: https://www.axios-http.cn/docs/interceptors steps1: 安装 npm install axios -ssteps2: /src/api/request.js 文件 >>> 拦截器 import axios from axios // 如果没用element-plus就不引入 import { ElMessage } from element-plusconst service axios.cre…...
【MetaGPT系列】【MetaGPT完全实践宝典——多智能体实践】
目录 前言一、智能体1-1、Agent概述1-2、Agent与ChatGPT的区别 二、多智能体框架MetaGPT2-1、安装&配置2-2、使用已有的Agent(ProductManager)2-3、多智能体系统介绍2-4、多智能体案例分析2-4-1、构建智能体团队2-4-2、动作/行为 定义2-4-3、角色/智…...
C#中GridControl的数据源双向绑定
1. 什么是双向数据绑定? 双向数据绑定是一种允许我们创建持久连接的技术,使模型数据和用户界面(UI)之间的交互能够自动同步。这意味着当模型数据发生变化时,UI会自动更新,反之亦然。这种双向数据绑定极大地简化了UI和模型数据之间…...
sklearn详细基础教程(科普篇)
Scikit-learn(简称sklearn)是Python中一个强大且易于使用的机器学习库,它基于NumPy、SciPy和matplotlib等Python库构建,提供了丰富的工具集,包括数据预处理、特征选择、模型训练、评估和预测等功能。以下是sklearn的详…...
el-table列的显示与隐藏
需求:实现 表字段的显示与隐藏。效果图 代码实现 写在前面 首先 我部分字段有自定义的排序逻辑,和默认值或者 数据的计算 所以是不能简单的使用 v-for 循环column 。然后 我需要默认展示一部分字段,并且 当表无数据时 提示不能 显示隐藏 …...
使用命令快速删除项目中的node_modules
描述 直接调用了系统自带的命令行工具,无需额外安装任何第三方库或工具。 同时,这些命令经过优化,能够快速处理大量文件,从而实现快速删除。 步骤 1、进入项目文件夹; 2、如果是Mac/Linux 环境下,执行&a…...
leetCode15三数之和(双指针)
目录 1、题目 2、思路 3、代码 4、总结 1、题目 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为…...
数据挖掘-数据预处理
来自🥬🐶程序员 Truraly | 田园 的博客,最新文章首发于:田园幻想乡 | 原文链接 | github (欢迎关注) 文章目录 3.3.1 数据的中心趋势平均数和加权平均数众数,中位数和均值描述数据的离散程度 &a…...
【调试笔记-20240723-Linux-gitee 仓库同步 github 仓库,并保持所有访问链接调整为指向 gitee 仓库的 URL】
调试笔记-系列文章目录 调试笔记-20240723-Linux-gitee 仓库同步 github 仓库,并保持所有访问链接调整为指向 gitee 仓库的 URL 文章目录 调试笔记-系列文章目录调试笔记-20240723-Linux-gitee 仓库同步 github 仓库,并保持所有访问链接调整为指向 gite…...
《GPT-4o mini:开启开发与创新的新纪元》
在科技发展的快速进程中,OpenAI 推出的 GPT-4o mini 模型如同一阵春风,给开发者们带来了新的希望和机遇。它以其卓越的性能和极具吸引力的价格,成为了行业内热议的焦点。 当我首次听闻 GPT-4o mini 的消息时,内心充满了好奇与期待…...
生成树协议配置与分析
前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 一、相关知识 1、生成树协议简介 生成树协议(STP)是一种避免数据链路层逻辑环路的机制,它通过信息交互识别环路并…...
Golang | Leetcode Golang题解之第287题寻找重复数
题目: 题解: func findDuplicate(nums []int) int {slow, fast : 0, 0for slow, fast nums[slow], nums[nums[fast]]; slow ! fast; slow, fast nums[slow], nums[nums[fast]] { }slow 0for slow ! fast {slow nums[slow]fast nums[fast]}return s…...
【音视频SDL2入门】创建第一个窗口
文章目录 前言创建窗口的流程需要使用的函数1. 初始化 SDL 库2. 创建 SDL 窗口3. 获取与窗口关联的表面SDL_FillRect 函数介绍4. 更新窗口表面5. 延迟一定时间6. 销毁窗口并退出 SDL 库示例代码总结 前言 SDL2(Simple DirectMedia Layer)是一个跨平台的…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
