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

经典目标检测YOLO系列(一)复现YOLOV1(4)VOC2007数据集的读取及预处理

经典目标检测YOLO系列(一)复现YOLOV1(4)VOC2007数据集的读取及预处理

之前,我们依据《YOLO目标检测》(ISBN:9787115627094)一书,提出了新的YOLOV1架构,并解决前向推理过程中的两个问题,继续按照此书进行YOLOV1的复现。
经典目标检测YOLO系列(一)YOLOV1的复现(1)总体架构

经典目标检测YOLO系列(一)复现YOLOV1(2)反解边界框及后处理

经典目标检测YOLO系列(一)复现YOLOV1(3)正样本的匹配及损失函数的实现

我们今天讲解下数据集的读取、数据集的预处理以及数据增强。

1、利用VOCDataset类读取数据集

对于目标检测任务而言,常用的数据集包括较小的PASCAL VOC以及较大的MS COCO。我们目前只需要了解并掌握使用较小的PASCAL VOC数据集即可,虽然COCO数据集是当下最主流的数据集之一,但其较大的数据量自然增加了训练成本。

VOC2007及2012数据集的下载(百度网盘)和介绍可以参考:

经典目标检测YOLO系列(1)YOLO-V1算法及其在VOC2007数据集上的应用

当然,作者在项目代码中,dataset/script文件中提供了用于下载VOC数据集的脚本。

1.1 VOCDataset类的实现

我们自定义VOCDataset类,继承pytorch提供的torch.utils.data.Dataset类,主要实现__getitem__函数。再利用pytorch提供的Dataloader,就可以通过调用__getitem__函数来批量读取VOC数据集图片和标签了。

VOCDataset类的初始化部分,如下方的代码所示:

# RT-ODLab\dataset\voc.pyclass VOCDataset(data.Dataset):def __init__(self, img_size     :int = 640,data_dir     :str = None,# image_sets   = [('2007', 'trainval'), ('2012', 'trainval')],image_sets   = [('2007', 'trainval')],trans_config = None,transform    = None,is_train     :bool = False,load_cache   :bool = False,):# ----------- Basic parameters -----------self.img_size = img_sizeself.image_set = image_setsself.is_train = is_trainself.target_transform = VOCAnnotationTransform()# ----------- Path parameters -----------self.root = data_dirself._annopath = osp.join('%s', 'Annotations', '%s.xml')self._imgpath = osp.join('%s', 'JPEGImages', '%s.jpg')# ----------- Data parameters -----------self.ids = list()for (year, name) in image_sets:rootpath = osp.join(self.root, 'VOC' + year)for line in open(osp.join(rootpath, 'ImageSets', 'Main', name + '.txt')):self.ids.append((rootpath, line.strip()))self.dataset_size = len(self.ids)# ----------- Transform parameters -----------self.transform = transformself.mosaic_prob = trans_config['mosaic_prob'] if trans_config else 0.0self.mixup_prob = trans_config['mixup_prob'] if trans_config else 0.0self.trans_config = trans_configprint('==============================')print('use Mosaic Augmentation: {}'.format(self.mosaic_prob))print('use Mixup Augmentation: {}'.format(self.mixup_prob))print('==============================')# ----------- Cached data -----------self.load_cache = load_cacheself.cached_datas = Noneif self.load_cache:self.cached_datas = self._load_cache()

VOCDataset类包含读取图片和标签的功能,对此,我们实现了相关的功能,如下方代码所示:

  • 通过调用pull_image和pull_anno两个函数来分别去读取图片和以XML格式保存的标签文件,load_image_target 函数最终会输出一张图片image,以及保存了该图片中的所有目标的边界框和类别信息的target。
  • 需要注意的是,当self.cached_datas不是None时,我们会从缓存了数据集所有数据的self.cached_datas中直接索引图片和对应的标签数据,而不用再从本地去读取了。
 # RT-ODLab\dataset\voc.py# ------------ Load data function ------------def load_image_target(self, index):# 读取图片和标签的功能# == 从缓存中进行加载 ==if self.cached_datas is not None:# load a datadata_item = self.cached_datas[index]image = data_item["image"]target = data_item["target"]# ==从磁盘中进行加载 ==else:        # load an image# 1、利用open-cv加载一张图像image, _ = self.pull_image(index)height, width, channels = image.shape# laod an annotation# 2、利用ET读取一张图片的标签信息(bbox以及类别信息)anno, _ = self.pull_anno(index)# guard against no boxes via resizinganno = np.array(anno).reshape(-1, 5)target = {"boxes": anno[:, :4],        # 一张图片中GT所有的bbox信息"labels": anno[:, 4],        # 一张图片中物体信息"orig_size": [height, width] # 原始图片的大小}# 返回一张图像及其标签信息return image, targetdef pull_image(self, index):# 利用opencv读取一张图片img_id = self.ids[index]# D:\\VOCdevkit\\VOC2007\\JPEGImages\\000001.jpgimage = cv2.imread(self._imgpath % img_id, cv2.IMREAD_COLOR)return image, img_iddef pull_anno(self, index):# 利用ET读取一张图片的标签信息(bbox以及类别信息)img_id = self.ids[index]# 'D:\\VOCdevkit\\VOC2007\\Annotations\\000001.xml'anno = ET.parse(self._annopath % img_id).getroot()# 解析xml文件,返回[[xmin,ymin,xmax,ymax,标签id],...]anno = self.target_transform(anno)return anno, img_id

这里作者为了实现了从缓冲中读取,实现了下面代码:

  • 代码中,将所有的图片和标签都保存在data_items变量中,注意,对于读取的每一张图片,我们都预先对其做resize操作,这是因为在后续的数据预处理环节中,我们会对原始图片先做一步resize操作,然后再去做其他的预处理操作,为了节省内存空间,这里我们就直接做好了。
  • 不过,就学习而言,我们是默认不采用这种cache方式,因为这对于设备的内存要求会很高。
    # RT-ODLab\dataset\voc.pydef _load_cache(self):data_items = []for idx in range(self.dataset_size):if idx % 2000 == 0:print("Caching images and targets : {} / {} ...".format(idx, self.dataset_size))# load a dataimage, target = self.load_image_target(idx)orig_h, orig_w, _ = image.shape# resize imager = self.img_size / max(orig_h, orig_w)if r != 1: interp = cv2.INTER_LINEARnew_size = (int(orig_w * r), int(orig_h * r))image = cv2.resize(image, new_size, interpolation=interp)img_h, img_w = image.shape[:2]# rescale bboxboxes = target["boxes"].copy()boxes[:, [0, 2]] = boxes[:, [0, 2]] / orig_w * img_wboxes[:, [1, 3]] = boxes[:, [1, 3]] / orig_h * img_htarget["boxes"] = boxesdict_item = {}dict_item["image"] = imagedict_item["target"] = targetdata_items.append(dict_item)return data_items

在实现了load_image_target函数后,我们再实现一个pull_item函数,在该函数中,我们会对读取进来的数据做预处理操作(先忽略预处理):

  • 代码中,我们会根据random.random()< self.mosaic_prob条件来决定是否读取马赛克图像,即将多张图像拼接在一起,使得拼接后的图像能拥有更丰富的目标信息。
  • 另外,我们也会根据random.random()< self.mixup_prob条件来决定是要加载混合图像,即使用混合增强(Mixup augmentation)技术随机将两张图片以加权求和的方式融合在一起。
  • 就目前的学习目标而言,我们暂时还不会使用到这两个过于强大的数据增强,因此mosaic_prob及mixup_prob默认为0。
  • 最后,外部的Dataloader就可以通过调用__getitem__函数来读取VOC数据集图片和标签了。
    # RT-ODLab\dataset\voc.pydef pull_item(self, index):# 实现一个pull_item函数,在该函数中,我们会对读取进来的数据做预处理操作if random.random() < self.mosaic_prob:# load a mosaic imagemosaic = Trueimage, target = self.load_mosaic(index)else:mosaic = False# load an image and targetimage, target = self.load_image_target(index)# MixUpif random.random() < self.mixup_prob:image, target = self.load_mixup(image, target)# augmentimage, target, deltas = self.transform(image, target, mosaic)return image, target, deltas# ------------ Basic dataset function ------------def __getitem__(self, index):image, target, deltas = self.pull_item(index)return image, target, deltasdef __len__(self):return self.dataset_size    

1.2 读取VOC数据集

1.2.1 build_dataset函数

这里,将读取VOC数据集封装为build_dataset函数,如下:

build_dataset函数:

# RT-ODLab\dataset\build.py# ------------------------------ Dataset ------------------------------
def build_dataset(args, data_cfg, trans_config, transform, is_train=True):# ------------------------- Basic parameters -------------------------data_dir = os.path.join(args.root, data_cfg['data_name'])num_classes = data_cfg['num_classes']class_names = data_cfg['class_names']class_indexs = data_cfg['class_indexs']dataset_info = {'num_classes': num_classes,'class_names': class_names,'class_indexs': class_indexs}# ------------------------- Build dataset -------------------------## VOC datasetif args.dataset == 'voc':image_sets = [('2007', 'trainval')] if is_train else [('2007', 'test')]dataset = VOCDataset(img_size     = args.img_size,data_dir     = data_dir,image_sets   = image_sets,transform    = transform,trans_config = trans_config,is_train     = is_train,load_cache   = args.load_cache)## COCO datasetelif args.dataset == 'coco':image_set = 'train2017' if is_train else 'val2017'dataset = COCODataset(img_size     = args.img_size,data_dir     = data_dir,image_set    = image_set,transform    = transform,trans_config = trans_config,is_train     = is_train,load_cache   = args.load_cache)## CrowdHuman datasetelif args.dataset == 'crowdhuman':image_set = 'train' if is_train else 'val'dataset = CrowdHumanDataset(img_size     = args.img_size,data_dir     = data_dir,image_set    = image_set,transform    = transform,trans_config = trans_config,is_train     = is_train,)## Custom datasetelif args.dataset == 'ourdataset':image_set = 'train' if is_train else 'val'dataset = OurDataset(data_dir     = data_dir,img_size     = args.img_size,image_set    = image_set,transform    = transform,trans_config = trans_config,s_train      = is_train,oad_cache    = args.load_cache)return dataset, dataset_info

1.2.2 build_dataloader函数

  • 在实现了Dataset以及数据预处理操作后,我们接下来就需要为训练中要用到的Dataloader做一些准备。
  • Dataloader的作用就是利用多线程来快速地为当前的训练迭代准备好一批数据,以便我们去做推理、标签分配和损失函数,这其中就要用到collate_fn方法,该方法的主要目的就是去将多个线程读取进来的数据处理成我们所需要的格式。
  • 默认情况下,Dataloader自带的该方法是直接将所有数据组成个更大的torch.Tensor,但这不适合于我们的数据,因为我们的标签数据是Dict,无法拼接成Tensor,因此,我们需要自己实现一个Collate函数,如下方的代码所示。
  • 这段代码的逻辑十分简单,就是从Dataloader利用多线程读取进来的一批数据batch, 分别去取出图片和标签,然后将图片组成一批数据,即torch.Tensor类型,其shape是[B, C, H, W],再将所有图片的target存放在一个List中,最后输出即可。
# RT-ODLab\utils\misc.py## collate_fn for dataloader
class CollateFunc(object):def __call__(self, batch):targets = []images = []for sample in batch:image = sample[0]target = sample[1]images.append(image)targets.append(target)images = torch.stack(images, 0) # [B, C, H, W]return images, targets
# batch为2的时候,值为下面所示:
[(tensor([[[0., 0., 0.,  ..., 0., 0., 0.],...,[0., 0., 0.,  ..., 0., 0., 0.]],[[0., 0., 0.,  ..., 0., 0., 0.],...,[0., 0., 0.,  ..., 0., 0., 0.]],[[0., 0., 0.,  ..., 0., 0., 0.],...,[0., 0., 0.,  ..., 0., 0., 0.]]]), {'boxes': tensor([[114., 295., 119., 312.],[ 29., 230., 148., 321.]]), 'labels': tensor([14., 18.]), 'orig_size': [281, 500]}, None), (tensor([[[0., 0., 0.,  ..., 0., 0., 0.],...,[0., 0., 0.,  ..., 0., 0., 0.]],[[0., 0., 0.,  ..., 0., 0., 0.],...,[0., 0., 0.,  ..., 0., 0., 0.]],[[0., 0., 0.,  ..., 0., 0., 0.],...,[0., 0., 0.,  ..., 0., 0., 0.]]]), {'boxes': tensor([[  0.,  79., 416., 362.]]), 'labels': tensor([1.]), 'orig_size': [375, 500]}, None)
]# 经过CollateFunc函数后转变为:
# images.shape:
torch.Size([2, 3, 416, 416])
# targets为:
[{'boxes': tensor([[114., 295., 119., 312.],[ 29., 230., 148., 321.]]), 'labels': tensor([14., 18.]), 'orig_size': [281, 500]}, {'boxes': tensor([[  0.,  79., 416., 362.]]), 'labels': tensor([1.]), 'orig_size': [375, 500]}
]

在写完了Collate函数后,我们就可以利用PyTorch框架提供的Dataloader来实现这部分的操作:

  • 当args.distributed=True时,我们会开启分布式训练,即所谓的“DDP”,此时我们就要构建DDP模块下的sampler,否则的话我们就构建单卡环境下的RandomSampler即可。
  • 然后构建读取一批数据的BatchSampler,其中,我们将drop_last设置为True,即当数dataloader读取到最后,发现剩下的数据数量少于我们设定的batch size,那么就丢掉这一批数据。
  • 由于每次dataloader读取完所有的数据后,即完成一次训练的epoch,内部数据会被重新打乱一次,因此这种丢弃方法不会造成负面影响。

build_dataloader函数:

# RT-ODLab\utils\misc.py# ---------------------------- For Dataset ----------------------------
## build dataloader
def build_dataloader(args, dataset, batch_size, collate_fn=None):# distributedif args.distributed:sampler = DistributedSampler(dataset)else:sampler = torch.utils.data.RandomSampler(dataset)batch_sampler_train = torch.utils.data.BatchSampler(sampler, batch_size, drop_last=True)# 读取VOC数据集dataloader = DataLoader(dataset, batch_sampler=batch_sampler_train,collate_fn=collate_fn, num_workers=args.num_workers, pin_memory=True)return dataloader

2、数据预处理

2.1 SSD风格的预处理

我们在构造VOCDataset类时候,需要传入transform,这就是数据的预处理。下面是构造transform的函数:

  • 在YOLOV1中,我们使用ssd风格数据预处理及数据增强策略,即trans_config[‘aug_type’]的值为ssd

  • 训练过程中,我们使用SSDAugmentation,即只采用SSD工作所用到的数据增强操作,包括随机裁剪、随机翻转、随机色彩空间变换、随机图像色彩变换等等。

  • 前向推理过程中,我们使用SSDBaseTransform,即前向推理过程中,只对图像做预处理操作。

  • 在YOLOV1中,我们关闭马赛克增强以及混合增强。

  • 我们可以运行dataset/voc.py文件,将数据增强后的图片可视化出来,增强的效果即可一目了然。读者可以参考下方的运行命令来查看。具体数据增强的代码实现,还请参考源码。

    python dataset/voc.py --root /data/VOCdevkit --aug_type ssd --is_train

# RT-ODLab\dataset\build.py# ------------------------------ Transform ------------------------------
def build_transform(args, trans_config, max_stride=32, is_train=False):# Modify trans_configif is_train:## mosaic prob.if args.mosaic is not None:trans_config['mosaic_prob']=args.mosaic if is_train else 0.0else:trans_config['mosaic_prob']=trans_config['mosaic_prob'] if is_train else 0.0## mixup prob.if args.mixup is not None:trans_config['mixup_prob']=args.mixup if is_train else 0.0else:trans_config['mixup_prob']=trans_config['mixup_prob']  if is_train else 0.0# Transformif trans_config['aug_type'] == 'ssd':if is_train:transform = SSDAugmentation(img_size=args.img_size,)else:transform = SSDBaseTransform(img_size=args.img_size,)trans_config['mosaic_prob'] = 0.0trans_config['mixup_prob'] = 0.0elif trans_config['aug_type'] == 'yolov5':if is_train:transform = YOLOv5Augmentation(img_size=args.img_size,trans_config=trans_config,use_ablu=trans_config['use_ablu'])else:transform = YOLOv5BaseTransform(img_size=args.img_size,max_stride=max_stride)return transform, trans_config

1.1.1 训练过程中的SSDAugmentation

  • 数据集固定,其所携带的各种信息便也就固定了下来,因此也就限定了模型的学习能力。为了扩充数据集的数量以及样本的丰富性、提高模型的鲁棒性和泛化能力,我们往往会在训练阶段对数据集已有的数据做随机的预处理操作,比如随机水平翻转、随机剪裁、色彩扰动、空间尺寸缩放等,这就是数据增强
  • 对于我们现在所要实现的YOLOv1,我们只采用SSD工作所用到的数据增强操作,包括随机裁剪、随机翻转、随机色彩空间变换、随机图像色彩变换等等。
  • 我们暂时不会用到更强大的马赛克增强、混合增强等手段。在我们实现的YOLOv1的配置文件中,我们可以看到’trans_type’: ‘ssd’ 字样,这就表明我们使用SSD风格的数据增强。
# RT-ODLab\dataset\data_augment\ssd_augment.py
# ----------------------- Main Functions -----------------------
## SSD-style Augmentation
class SSDAugmentation(object):def __init__(self, img_size=640):self.img_size = img_sizeself.augment = Compose([ConvertFromInts(),                         # 将int类型转换为float32类型PhotometricDistort(),                      # 图像颜色增强Expand(),                                  # 扩充增强RandomSampleCrop(),                        # 随机剪裁RandomHorizontalFlip(),                    # 随机水平翻转Resize(self.img_size)                      # resize操作])def __call__(self, image, target, mosaic=False):boxes = target['boxes'].copy()labels = target['labels'].copy()deltas = None# augmentimage, boxes, labels = self.augment(image, boxes, labels)# to tensorimg_tensor = torch.from_numpy(image).permute(2, 0, 1).contiguous().float()target['boxes'] = torch.from_numpy(boxes).float()target['labels'] = torch.from_numpy(labels).float()return img_tensor, target, deltas

1.1.2 前向推理过程中的SSDBaseTransform

  • 前向推理过程中,只对图像做预处理操作。

  • 首先,对于给定的一张图片image,我们调用opencv提供的cv2.resize函数将其空间尺寸变换到指定的图像尺寸,比如416x416。

    • 注意,经过这么一次操作,原始图像的长宽比通常会被改变,使得图片发生一定的畸变。大多数时候这一问题并不严重,但对于某些场景来说,这种畸变可能会破坏模型对真实世界的认识。
    • 因此,在后来的YOLO工作里,采用了保留长宽比的Resize操作。
  • 需要注意的是,我们没有在这里对图像做归一化操作,这一操作我们后在训练部分的代码中再做

  • 在完成了对图像的Resize操作后,我们也需要对相应的边界框坐标也做必要的调整,因为边界框坐标是相对于图片的,既然图片的尺寸都改变了,边界框坐标也必须做相应的比例变换。最后,我们将标签数据全部转换为torch.Tensor类型,以便后续的处理。

 # RT-ODLab\dataset\data_augment\ssd_augment.py## SSD-style valTransform
class SSDBaseTransform(object):def __init__(self, img_size):self.img_size = img_sizedef __call__(self, image, target=None, mosaic=False):deltas = None# resizeorig_h, orig_w = image.shape[:2]image = cv2.resize(image, (self.img_size, self.img_size)).astype(np.float32)# scale targetsif target is not None:boxes = target['boxes'].copy()labels = target['labels'].copy()img_h, img_w = image.shape[:2]boxes[..., [0, 2]] = boxes[..., [0, 2]] / orig_w * img_wboxes[..., [1, 3]] = boxes[..., [1, 3]] / orig_h * img_htarget['boxes'] = boxes# to tensorimg_tensor = torch.from_numpy(image).permute(2, 0, 1).contiguous().float()if target is not None:target['boxes'] = torch.from_numpy(boxes).float()target['labels'] = torch.from_numpy(labels).float()return img_tensor, target, deltas

至此,我们讲完了数据预处理操作,接下来,我们就可以在开始训练我们实现的YOLOv1模型。

相关文章:

经典目标检测YOLO系列(一)复现YOLOV1(4)VOC2007数据集的读取及预处理

经典目标检测YOLO系列(一)复现YOLOV1(4)VOC2007数据集的读取及预处理 之前&#xff0c;我们依据《YOLO目标检测》(ISBN:9787115627094)一书&#xff0c;提出了新的YOLOV1架构&#xff0c;并解决前向推理过程中的两个问题&#xff0c;继续按照此书进行YOLOV1的复现。 经典目标检…...

Android Studio xml布局代码补全功能失效问题

这里写目录标题 前言&#xff1a;问题描述原因分析&#xff1a;解决方案&#xff1a;1.更新 Android Studio 版本2.原版本解决XML补全失效 小结 前言&#xff1a; 在开发过程中&#xff0c;你可能遇到很多奇奇怪怪的问题。Android Studio 编译器出现问题也是常有的事情&#x…...

算法每日一题:队列中可以看到的人数 | 单调栈

大家好&#xff0c;我是星恒 今天是一道困难题&#xff0c;他的题解比较好理解&#xff0c;但是不好想出来&#xff0c;接下来就让我带大家来捋一捋这道题的思路&#xff0c;以及他有什么特征 题目&#xff1a;leetcode 1944有 n 个人排成一个队列&#xff0c;从左到右 编号为 …...

报表控件Stimulsoft 2023回顾:都做了哪些产品的改变?

在2023年过去一年中&#xff0c;报表控件Stimulsoft 针各类控件都做了重大改变&#xff0c;其中新增了某些产品、同时加强了很多产品的性能和UI设计&#xff0c;更加符合开发者需求&#xff0c;下面就跟随小编一起来回顾&#xff0c;具体都有哪些↓↓↓ Stimulsoft Ultimate &…...

Mybatis缓存实现方式

文章目录 装饰器模式Cache 接口及核心实现Cache 接口装饰器1. BlockingCache2. FifoCache3. LruCache4. SoftCache5. WeakCache 小结 缓存是优化数据库性能的常用手段之一&#xff0c;我们在实践中经常使用的是 Memcached、Redis 等外部缓存组件&#xff0c;很多持久化框架提供…...

C#用StringBuilder高效处理字符串

目录 一、背景 二、使用StringBuilder便捷、高效地操作字符串 三、实例 1.源码 2.生成效果 四、实例中知识点 1.StringBuilder 构造函数 &#xff08;1&#xff09;定义 &#xff08;2&#xff09;重载 &#xff08;3&#xff09;StringBuilder() &#xff08;4&…...

python开发案例教程-清华大学出版社(张基温)答案(4.2)

目录 练习 4.2 1. 代码分析题 2. 程序设计题 练习 4.2 1. 代码分析题 阅读下面的代码&#xff0c;给出输出结果。 &#xff08;1&#xff09; class A:def __init__(self,a,b,c):self.xabca A(3,5,7);b getattr(a,x);setattr(a,x,b3);print(a.x)18 &#xff08;2&…...

【MATLAB】【数字信号处理】线性卷积和抽样定理

已知有限长序列&#xff1a;xk1,2,1,1,0,-3, hk[1,-1,1] , 计算离散卷积和ykxk*h(k) 。 程序如下&#xff1a; function [t,x] My_conv(x1,x2,t1,t2,dt) %文件名与函数名对应 %自写的卷积函数 x conv(x1,x2)*dt; t0 t1(1) t2(1); L length(x1) length(x2)-2; t t0:dt…...

什么是 MVVM ?

课堂笔记 什么是 MVVM &#xff1f; MVVM 是一种架构模式&#xff0c;它最初是由微软的两位工程师在 2005 年的时候所提出的。 Model&#xff1a;Model代表的是你的数据View&#xff1a;视图&#xff0c;直接和用户打交道的ViewModel&#xff1a;ViewModel 是 View 和 Model…...

Redis(一)

1、redis Redis是一个完全开源免费的高性能&#xff08;NOSQL&#xff09;的key-value数据库。它遵守BSD协议&#xff0c;使用ANSI C语言编写&#xff0c;并支持网络和持久化。Redis拥有极高的性能&#xff0c;每秒可以进行11万次的读取操作和8.1万次的写入操作。它支持丰富的数…...

自动驾驶预测-决策-规划-控制学习(1):自动驾驶框架、硬件、软件概述

文章目录 前言&#xff1a;无人驾驶分级一、不同level的无人驾驶实例分析1.L2级别2.L3级别3.L4级别①如何在减少成本的情况下&#xff0c;实现类似全方位高精度的感知呢&#xff1f;②路侧终归是辅助&#xff0c;主车的智能才是重中之重&#xff1a;融合深度学习 二、无人驾驶的…...

SSM建材商城网站----计算机毕业设计

项目介绍 本项目分为前后台&#xff0c;前台为普通用户登录&#xff0c;后台为管理员登录&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,管理员管理,注册用户管理,新闻公告管理,建材类型管理,配货点管理,建材商品管理,建材订单管理,建材评价管理等功能。 用…...

js逆向第9例:猿人学第2题-js混淆-动态cookie1

题目2:提取全部5页发布日热度的值,计算所有值的加和,并提交答案 (感谢蔡老板为本题提供混淆方案) 既然题目已经给出了cookie问题,那就从cookie入手,控制台找到数据请求地址 可以看到如下加密字符串m类似md5,后面跟着时间戳 m=45cc41dcdb15159ebb50564635f8e362|1704301…...

[论文分享]TimesURL:通用时间序列表示学习的自监督对比学习

论文题目&#xff1a;TimesURL: Self-supervised Contrastive Learning for Universal Time Series Representation Learning 论文地址&#xff1a;https://arxiv.org/abs/2312.15709 代码地址&#xff1a;暂无 摘要 学习适用于各种下游任务的通用时间序列表示具有挑战性&…...

解决sublime中文符号乱码问题

效果图 原来 后来 问题不是出自encode文件编码&#xff0c;而是win10的字体问题。 解决方法 配置&#xff1a; { "font_face":"Microsoft Yahei", "dpi_scale": 1.0 } 参考自 Sublime 输入中文显示方框问号乱码_sublime中文问号-CSDN博…...

厚积薄发11年,鸿蒙究竟有多可怕

​12月20日中国工程院等权威单位发布《2023年全球十大工程成就》。本次发布的2023全球十大工程成就包括“鸿蒙操作系统”在内。入围的“全球十大工程成就”&#xff0c;主要指过去五年由世界各国工程科技工作者合作或单独完成且实践验证有效的&#xff0c;并且已经产生全球影响…...

pyDAL一个python的ORM(4) pyDAL查询操作

1 、简单查询 rows db(db.person.dept marketing).select(db.person.id, db.person.name, db.person.dept) rows db(db.person.dept marketing).select() rows db(db.person.dept marketing).select(db.person.ALL) rows db().select(db.person.ALL) / db(db.person).se…...

如何通过Python将各种数据写入到Excel工作表

在数据处理和报告生成等工作中&#xff0c;Excel表格是一种常见且广泛使用的工具。然而&#xff0c;手动将大量数据输入到Excel表格中既费时又容易出错。为了提高效率并减少错误&#xff0c;使用Python编程语言来自动化数据写入Excel表格是一个明智的选择。Python作为一种简单易…...

跟着cherno手搓游戏引擎【2】:日志系统spdlog和premake的使用

配置&#xff1a; 日志库文件github&#xff1a; GitHub - gabime/spdlog: Fast C logging library. 新建vendor文件夹 将下载好的spdlog放入 配置YOTOEngine的附加包含目录&#xff1a; 配置Sandbox的附加包含目录&#xff1a; 包装spdlog&#xff1a; 在YOTO文件夹下创建…...

Ubuntu20.04 上启用 VCAN 用作本地调试

目录 一、启用本机的 VCAN​ 编辑 1.1 加载本机的 vcan 1.2 添加本机的 vcan0 1.3 查看添加的 vcan0 1.4 开启本机的 vcan0 1.5 关闭本机的 vcan0 1.6 删除本机的 vcan0 二、测试本机的 VCAN 2.1 CAN 发送数据 代码 2.2 CAN 接收数据 代码 2.3 CMakeLists.…...

LeetCode(31) 下一个排列

整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如&#xff0c;arr [1,2,3] &#xff0c;以下这些都可以视作 arr 的排列&#xff1a;[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。 整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地&#xf…...

Git LFS: 简单高效的大文件版本控制

Git Large File Storage 问题 在使用git上传大文件时候&#xff0c;git push时候会报错: remote: error: File xxx.tar.gz is 135.17 MB; this exceeds GitHubs file size limit of 100 MB可以看到&#xff0c;git限制上传大小是100MB&#xff0c;超过的话就会报错&#xff…...

如何培养用户思维

产品开发是根据用户要求建造出系统的过程&#xff0c;产品开发是一项包括需求捕捉、需求分析、设计、实现和测试的系统工程&#xff0c;一般通过某种程序设计语言来实现。然而用户思维能够帮助企业更好地理解市场需求&#xff0c;进行产品的开发和完善&#xff0c;用户是企业产…...

由浅入深理解C#中的事件

目录 本文较长&#xff0c;给大家提供了目录&#xff0c;可以直接看自己感兴趣的部分。 前言有关事件的概念示例​ 简单示例​ 标准 .NET 事件模式​ 使用泛型版本的标准 .NET 事件模式​ 补充总结 参考前言 前面介绍了C#中的委托&#xff0c;事件的很多部分都与委托…...

Nginx(十六) 配置文件详解 - server stream服务流

本篇文章主要讲 ngx_stream_core_module 模块下各指令的使用方法&#xff0c;Nginx默认未配置该模块&#xff0c;需要用“--with-stream”配置参数重新编译Nginx。 worker_processes auto;error_log /var/log/nginx/error.log info;events {worker_connections 1024; }stream…...

Css中默认与继承

initial默认样式&#xff1a; initial 用于设置 Css 属性为默认值 h1 {color: initial; }如display或position不能被设置为initial&#xff0c;因为有默认属性。例如&#xff1a;display:inline inherit继承样式&#xff1a; inherit 用于设置 Css 属性应从父元素继承 di…...

gitee上的vue大屏项目

在 Gitee 上,有几个值得注意的 Vue 大屏项目:vue-big-screen-plugin (Gitee): 这是一个基于 Vue3、Typescript、DataV 和 ECharts5 框架的可视化大屏项目。它使用 .vue 和 .tsx 文件构建界面,并采用新版动态屏幕适配方案。这个项目支持数据的动态刷新渲染,内部的 DataV 和 …...

【LeetCode:114. 二叉树展开为链表 | 二叉树 + 递归】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…...

社保养老金发放计算方法

退休后养老金计算公式很复杂&#xff0c;自己自行百度查一下&#xff0c;这里说一下男性&#xff0c;女工人&#xff0c;女干部之间计算差别。 退休后&#xff0c;能到手的养老金多少&#xff0c;取决于你的个人账户里的钱&#xff0c;个人账户里的钱越多&#xff0c;到手养老…...

概率论基础复习题

一、填空题 二、选择题 答案&#xff1a;B 答案&#xff1a;C 答案&#xff1a;C 答案&#xff1a;D。统计量不含任何未知参数。 答案&#xff1a;A 答案&#xff1a;C 样本均值是总体均值的无偏估计&#xff1b;样本方差是总体方差的无偏估计。 答案&#xff1a;B。统计值是一…...

域名注册平台的网站怎么做/电脑培训网上课程

一、微信小程序运行环境 微信小程序的 javascript 运行环境和浏览器不同&#xff0c;页面的脚本逻辑是在JsCore中运行&#xff0c;JsCore是一个没有窗口对象的环境&#xff0c;所以不能在脚本中使用window&#xff0c;也无法在脚本中操作组件&#xff0c;JsCore中也没有 Xmlht…...

网站标题的重要性/网站优化方案

一、Java平台体系及应用场景从1995年Sun Microsystems公司正式推出Java&#xff0c;到2006年时Sun公司将其开源&#xff0c;迄今为止已经有了20年的历史。Java本身已不仅仅只是一门面向对象的编程语言&#xff0c;而是由一系列计算机软件和规范形成的技术体系&#xff0c;这个技…...

可以免费做演播的听书网站/龙岗网站建设

前言我们在上一篇文章 中以实例讲解如何定义和使用 lambda 表达式&#xff0c;以及与其它语言相比&#xff0c;lambda 表达式在 Java 中的特殊规范。并且提到&#xff0c;lambda 表达式可以进一步简化为函数引用。这篇文章将介绍如何使用函数引用&#xff0c;话不多说了&#x…...

高端网站鉴赏/免费引流微信推广

有时候共享了文件夹,别人却无法访问,有可能是因为对方访问的用户没有密码导致的,给访问用户设置密码就可以. 但是有时候由于各种原因,不能给用户设置密码,这可怎么办? 其实这是一个安全策略导致的,该策略要求:使用空密码的账户只能通过控制台登陆. 把它关了就行,它在这里: 转载…...

wordpress会员可见/域名服务器ip查询网站

本节内容只有通过例题来记录效果才是最好的,请看下面内容&#xff01; 递归实现二分法 经典二分查找问题&#xff1a;LintCode 炼码 描述**&#xff1a;**在一个排序数组中找一个数&#xff0c;返回该数出现的任意位置&#xff0c;如果不存在&#xff0c;返回 -1。 输入&…...

智能响应式网站建设/海南快速seo排名优化

AllowOverride是指确定允许存在于.htaccess文件中的指令类型 通常利用Apache的rewrite模块对 URL 进行重写的时候&#xff0c; rewrite规则会写在 .htaccess 文件里。但要使 apache 能够正常的读取.htaccess 文件的内容&#xff0c;就必须[1]对.htaccess 所在目录进行配置。从安…...