Yolov1 源码讲解 voc.py

news2024/7/6 17:38:20

先看结构

1.mean_rgb是voc2007专用的均值

voc2007分别是这样的

坐标格式(X0,Y0,X1,Y1)其中X0,Y0是左上角的坐标,X1,Y1是右下角的坐标。

coco,voc ,yolo数据集中的bbox的坐标格式_coco bbox格式_十二耳环的博客-CSDN博客

读取文本分割内容放进self.paths, self.boxes, self.labels = [], [], []  读的都是原始内容

self.paths [p1,p2,p3,..]

self.boxes[[x11,y11,x12,xy12],[x21,y21,x22,xy22],[x31,y31,x32,xy32],.......]

self.labels [l1,l2,l3,..]


2.boxes /= torch.Tensor([[w, h, w, h]]).expand_as(boxes) # normalize (x1, y1, x2, y2) w.r.t. image width/height. 相对图片大小归一化

扩展成多少个框,然后相对应最后归一化 torch.Tensor([[w, h, w, h]]).expand_as(boxes)

encode那句下面说 所以传进去的boxes是已经相对图片大小归一化的左上坐标和右下坐标

def __getitem__(self, idx):
        path = self.paths[idx]#'C:\\Pro\\pythonProject\\yolov1_pytorch-main_chunsun\\Datasets/VOCdevkit/VOC2007/JPEGImages/003659.jpg'
        img = cv2.imread(path)#375*500*3  就是文件大小
        boxes = self.boxes[idx].clone() # [n, 4]
        labels = self.labels[idx].clone() # [n,]

        if self.is_train:
            #img, boxes = self.random_flip(img, boxes)
            #img, boxes = self.random_scale(img, boxes)

            img = self.random_blur(img)
            img = self.random_brightness(img)
            img = self.random_hue(img)
            img = self.random_saturation(img)

            img, boxes, labels = self.random_shift(img, boxes, labels)
            img, boxes, labels = self.random_crop(img, boxes, labels)

        # For debug.
        debug_dir = 'tmp/voc_tta'
        os.makedirs(debug_dir, exist_ok=True)
        img_show = img.copy()
        box_show = boxes.numpy().reshape(-1)
        n = len(box_show) // 4
        for b in range(n):#第几个框 一个框时候 b只有0 这种情况下
            pt1 = (int(box_show[4*b + 0]), int(box_show[4*b + 1]))#pt是坐标点
            pt2 = (int(box_show[4*b + 2]), int(box_show[4*b + 3]))
            cv2.rectangle(img_show, pt1=pt1, pt2=pt2, color=(0,255,0), thickness=1)
        cv2.imwrite(os.path.join(debug_dir, 'test_{}.jpg'.format(idx)), img_show)

        h, w, _ = img.shape
        boxes /= torch.Tensor([[w, h, w, h]]).expand_as(boxes) # normalize (x1, y1, x2, y2) w.r.t. image width/height. 相对图片大小归一化
        target = self.encode(boxes, labels) # [S, S, 5 x B + C]

        img = cv2.resize(img, dsize=(self.image_size, self.image_size), interpolation=cv2.INTER_LINEAR)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # assuming the model is pretrained with RGB images.
        img = (img - self.mean) / 255.0 # normalize from -1.0 to 1.0.
        img = self.to_tensor(img)

        return img, target

3.target = self.encode(boxes, labels) # [S, S, 5 x B + C]

boxes_wh 相对图片归一化的w,h

boxes_xy 真实框中心点的xy 而不是cell中心点的xy 相对图片归一化

循环中:  ceil是向上取整,再-1其实就是向下取整

xy从boxes_xy取对应真实框, 也是相对图片归一化

ij = (xy / cell_size).ceil() - 1.0,获得在哪个cell的行和列的s i,j 从上从左数分别第几个cell

x0y0 cell基准左上坐标 这里是相对图片归一化大小

xy_normalized 此处(xy - x0y0)得到溢出这个cell的值,再/cell_size也就是 相对图片归一化

最后遍历B次 target中所有同一个cell的所有B全是一样的值,也就是yolov1中

target 30维前两个框 值都一样

target构成5+5+20  5是(相对所处的这个cell的x,y相对于图片归一化的偏移量,w,h:是相对于图片归一化的)  20是只有一个类别(某个为1 其他19全为0)

    def encode(self, boxes, labels):
        """ Encode box coordinates and class labels as one target tensor.
        Args:
            boxes: (tensor) [[x1, y1, x2, y2]_obj1, ...], normalized from 0.0 to 1.0 w.r.t. image width/height.
            labels: (tensor) [c_obj1, c_obj2, ...]
        Returns:
            An encoded tensor sized [S, S, 5 x B + C], 5=(x, y, w, h, conf)
        """

        S, B, C = self.S, self.B, self.C
        N = 5 * B + C

        target = torch.zeros(S, S, N)
        cell_size = 1.0 / float(S)
        boxes_wh = boxes[:, 2:] - boxes[:, :2] # width and height for each box, [n, 2]
        boxes_xy = (boxes[:, 2:] + boxes[:, :2]) / 2.0 # center x & y for each box, [n, 2]
        for b in range(boxes.size(0)):
            xy, wh, label = boxes_xy[b], boxes_wh[b], int(labels[b])

            ij = (xy / cell_size).ceil() - 1.0
            i, j = int(ij[0]), int(ij[1]) # y & x index which represents its location on the grid.
            x0y0 = ij * cell_size # x & y of the cell left-top corner.
            xy_normalized = (xy - x0y0) / cell_size # x & y of the box on the cell, normalized from 0.0 to 1.0.

            # TBM, remove redundant dimensions from target tensor.
            # To remove these, loss implementation also has to be modified.
            for k in range(B):#两个框都搞一样的
                s = 5 * k
                target[j, i, s  :s+2] = xy_normalized#相对于cell归一化
                target[j, i, s+2:s+4] = wh
                target[j, i, s+4    ] = 1.0
            target[j, i, 5*B + label] = 1.0

        return target

 

剩下是随即裁剪部分 不太需要详细理解

import torch
from torch.utils.data import Dataset
import torchvision.transforms as transforms

import os
import random
import numpy as np
import cv2

class VOCDataset(Dataset):

    def __init__(self, is_train, image_dir, label_txt, image_size=448, grid_size=7, num_bboxes=2, num_classes=20):
        self.is_train = is_train
        self.image_size = image_size

        self.S = grid_size
        self.B = num_bboxes
        self.C = num_classes

        mean_rgb = [122.67891434, 116.66876762, 104.00698793]
        self.mean = np.array(mean_rgb, dtype=np.float32)

        self.to_tensor = transforms.ToTensor()

        if isinstance(label_txt, list) or isinstance(label_txt, tuple):
            # cat multiple list files together.
            # This is useful for VOC2007/VOC2012 combination.
            tmp_file = '/tmp/label.txt'
            os.system('cat %s > %s' % (' '.join(label_txt), tmp_file))
            label_txt = tmp_file

        self.paths, self.boxes, self.labels = [], [], []

        with open(label_txt) as f:
            lines = f.readlines()

        for line in lines:
            splitted = line.strip().split()
            fname = splitted[0]
            path = fname
            self.paths.append(path)
            boxes = np.array([np.array(list(map(int, box.split(',')))) for box in splitted[1:]])
            box, label = [], []
            for bbox in boxes:                
                box.append(bbox[:4])
                label.append(bbox[4])
            self.boxes.append(torch.Tensor(box))
            self.labels.append(torch.LongTensor(label))

        self.num_samples = len(self.paths)

    def __getitem__(self, idx):
        path = self.paths[idx]#'C:\\Pro\\pythonProject\\yolov1_pytorch-main_chunsun\\Datasets/VOCdevkit/VOC2007/JPEGImages/003659.jpg'
        img = cv2.imread(path)#375*500*3  就是文件大小
        boxes = self.boxes[idx].clone() # [n, 4]
        labels = self.labels[idx].clone() # [n,]

        if self.is_train:
            #img, boxes = self.random_flip(img, boxes)
            #img, boxes = self.random_scale(img, boxes)

            img = self.random_blur(img)
            img = self.random_brightness(img)
            img = self.random_hue(img)
            img = self.random_saturation(img)

            img, boxes, labels = self.random_shift(img, boxes, labels)
            img, boxes, labels = self.random_crop(img, boxes, labels)

        # For debug.
        debug_dir = 'tmp/voc_tta'
        os.makedirs(debug_dir, exist_ok=True)
        img_show = img.copy()
        box_show = boxes.numpy().reshape(-1)
        n = len(box_show) // 4
        for b in range(n):#第几个框 一个框时候 b只有0 这种情况下
            pt1 = (int(box_show[4*b + 0]), int(box_show[4*b + 1]))#pt是坐标点
            pt2 = (int(box_show[4*b + 2]), int(box_show[4*b + 3]))
            cv2.rectangle(img_show, pt1=pt1, pt2=pt2, color=(0,255,0), thickness=1)
        cv2.imwrite(os.path.join(debug_dir, 'test_{}.jpg'.format(idx)), img_show)

        h, w, _ = img.shape
        boxes /= torch.Tensor([[w, h, w, h]]).expand_as(boxes) # normalize (x1, y1, x2, y2) w.r.t. image width/height. 相对图片大小归一化
        target = self.encode(boxes, labels) # [S, S, 5 x B + C]

        img = cv2.resize(img, dsize=(self.image_size, self.image_size), interpolation=cv2.INTER_LINEAR)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # assuming the model is pretrained with RGB images.
        img = (img - self.mean) / 255.0 # normalize from -1.0 to 1.0.
        img = self.to_tensor(img)

        return img, target

    def __len__(self):
        return self.num_samples

    def encode(self, boxes, labels):
        """ Encode box coordinates and class labels as one target tensor.
        Args:
            boxes: (tensor) [[x1, y1, x2, y2]_obj1, ...], normalized from 0.0 to 1.0 w.r.t. image width/height.
            labels: (tensor) [c_obj1, c_obj2, ...]
        Returns:
            An encoded tensor sized [S, S, 5 x B + C], 5=(x, y, w, h, conf)
        """

        S, B, C = self.S, self.B, self.C
        N = 5 * B + C

        target = torch.zeros(S, S, N)
        cell_size = 1.0 / float(S)
        boxes_wh = boxes[:, 2:] - boxes[:, :2] # width and height for each box, [n, 2]
        boxes_xy = (boxes[:, 2:] + boxes[:, :2]) / 2.0 # center x & y for each box, [n, 2]
        for b in range(boxes.size(0)):
            xy, wh, label = boxes_xy[b], boxes_wh[b], int(labels[b])

            ij = (xy / cell_size).ceil() - 1.0
            i, j = int(ij[0]), int(ij[1]) # y & x index which represents its location on the grid.
            x0y0 = ij * cell_size # x & y of the cell left-top corner.
            xy_normalized = (xy - x0y0) / cell_size # x & y of the box on the cell, normalized from 0.0 to 1.0.

            # TBM, remove redundant dimensions from target tensor.
            # To remove these, loss implementation also has to be modified.
            for k in range(B):#两个框都搞一样的
                s = 5 * k
                target[j, i, s  :s+2] = xy_normalized#相对于cell归一化
                target[j, i, s+2:s+4] = wh
                target[j, i, s+4    ] = 1.0
            target[j, i, 5*B + label] = 1.0

        return target

    def random_flip(self, img, boxes):
        if random.random() < 0.5:
            return img, boxes

        h, w, _ = img.shape

        img = np.fliplr(img)

        x1, x2 = boxes[:, 0], boxes[:, 2]
        x1_new = w - x2
        x2_new = w - x1
        boxes[:, 0], boxes[:, 2] = x1_new, x2_new

        return img, boxes

    def random_scale(self, img, boxes):
        if random.random() < 0.5:
            return img, boxes

        scale = random.uniform(0.8, 1.2)
        h, w, _ = img.shape
        img = cv2.resize(img, dsize=(int(w * scale), h), interpolation=cv2.INTER_LINEAR)

        scale_tensor = torch.FloatTensor([[scale, 1.0, scale, 1.0]]).expand_as(boxes)
        boxes = boxes * scale_tensor

        return img, boxes

    def random_blur(self, bgr):
        if random.random() < 0.5:
            return bgr

        ksize = random.choice([2, 3, 4, 5])
        bgr = cv2.blur(bgr, (ksize, ksize))
        return bgr

    def random_brightness(self, bgr):
        if random.random() < 0.5:
            return bgr

        hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV)
        h, s, v = cv2.split(hsv)
        adjust = random.uniform(0.5, 1.5)
        v = v * adjust
        v = np.clip(v, 0, 255).astype(hsv.dtype)
        hsv = cv2.merge((h, s, v))
        bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

        return bgr

    def random_hue(self, bgr):
        if random.random() < 0.5:
            return bgr

        hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV)
        h, s, v = cv2.split(hsv)
        adjust = random.uniform(0.8, 1.2)
        h = h * adjust
        h = np.clip(h, 0, 255).astype(hsv.dtype)
        hsv = cv2.merge((h, s, v))
        bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

        return bgr

    def random_saturation(self, bgr):
        if random.random() < 0.5:
            return bgr

        hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV)
        h, s, v = cv2.split(hsv)
        adjust = random.uniform(0.5, 1.5)
        s = s * adjust
        s = np.clip(s, 0, 255).astype(hsv.dtype)
        hsv = cv2.merge((h, s, v))
        bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

        return bgr

    def random_shift(self, img, boxes, labels):
        if random.random() < 0.5:
            return img, boxes, labels

        center = (boxes[:, 2:] + boxes[:, :2]) / 2.0

        h, w, c = img.shape
        img_out = np.zeros((h, w, c), dtype=img.dtype)
        mean_bgr = self.mean[::-1]
        img_out[:, :] = mean_bgr

        dx = random.uniform(-w*0.2, w*0.2)
        dy = random.uniform(-h*0.2, h*0.2)
        dx, dy = int(dx), int(dy)

        if dx >= 0 and dy >= 0:
            img_out[dy:, dx:] = img[:h-dy, :w-dx]
        elif dx >= 0 and dy < 0:
            img_out[:h+dy, dx:] = img[-dy:, :w-dx]
        elif dx < 0 and dy >= 0:
            img_out[dy:, :w+dx] = img[:h-dy, -dx:]
        elif dx < 0 and dy < 0:
            img_out[:h+dy, :w+dx] = img[-dy:, -dx:]

        center = center + torch.FloatTensor([[dx, dy]]).expand_as(center) # [n, 2]
        mask_x = (center[:, 0] >= 0) & (center[:, 0] < w) # [n,]
        mask_y = (center[:, 1] >= 0) & (center[:, 1] < h) # [n,]
        mask = (mask_x & mask_y).view(-1, 1) # [n, 1], mask for the boxes within the image after shift.

        boxes_out = boxes[mask.expand_as(boxes)].view(-1, 4) # [m, 4]
        if len(boxes_out) == 0:
            return img, boxes, labels
        shift = torch.FloatTensor([[dx, dy, dx, dy]]).expand_as(boxes_out) # [m, 4]

        boxes_out = boxes_out + shift
        boxes_out[:, 0] = boxes_out[:, 0].clamp_(min=0, max=w)
        boxes_out[:, 2] = boxes_out[:, 2].clamp_(min=0, max=w)
        boxes_out[:, 1] = boxes_out[:, 1].clamp_(min=0, max=h)
        boxes_out[:, 3] = boxes_out[:, 3].clamp_(min=0, max=h)

        labels_out = labels[mask.view(-1)]

        return img_out, boxes_out, labels_out

    def random_crop(self, img, boxes, labels):
        if random.random() < 0.5:
            return img, boxes, labels

        center = (boxes[:, 2:] + boxes[:, :2]) / 2.0

        h_orig, w_orig, _ = img.shape
        h = random.uniform(0.6 * h_orig, h_orig)
        w = random.uniform(0.6 * w_orig, w_orig)
        y = random.uniform(0, h_orig - h)
        x = random.uniform(0, w_orig - w)
        h, w, x, y = int(h), int(w), int(x), int(y)

        center = center - torch.FloatTensor([[x, y]]).expand_as(center) # [n, 2]
        mask_x = (center[:, 0] >= 0) & (center[:, 0] < w) # [n,]
        mask_y = (center[:, 1] >= 0) & (center[:, 1] < h) # [n,]
        mask = (mask_x & mask_y).view(-1, 1) # [n, 1], mask for the boxes within the image after crop.

        boxes_out = boxes[mask.expand_as(boxes)].view(-1, 4) # [m, 4]
        if len(boxes_out) == 0:
            return img, boxes, labels
        shift = torch.FloatTensor([[x, y, x, y]]).expand_as(boxes_out) # [m, 4]

        boxes_out = boxes_out - shift
        boxes_out[:, 0] = boxes_out[:, 0].clamp_(min=0, max=w)
        boxes_out[:, 2] = boxes_out[:, 2].clamp_(min=0, max=w)
        boxes_out[:, 1] = boxes_out[:, 1].clamp_(min=0, max=h)
        boxes_out[:, 3] = boxes_out[:, 3].clamp_(min=0, max=h)

        labels_out = labels[mask.view(-1)]
        img_out = img[y:y+h, x:x+w, :]

        return img_out, boxes_out, labels_out


def test():
    from torch.utils.data import DataLoader

    #image_dir = r'D:\Yolo\yolov1_pytorch-main\Datasets\VOCdevkit\VOC2007\JPEGImages'
    image_dir = r'C:\Pro\pythonProject\yolov1_pytorch-main_chunsun\Datasets\VOCdevkit\VOC2007\JPEGImages'
    #label_txt = ['data/voc2007.txt']
    label_txt = 'data/2007_train.txt'

    dataset = VOCDataset(True, image_dir, label_txt)
    data_loader = DataLoader(dataset, batch_size=1, shuffle=False, num_workers=0)

    data_iter = iter(data_loader)
    for i in range(100):
        img, target = next(data_iter)
        print(img.size(), target.size())

if __name__ == '__main__':
    test()

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

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

相关文章

Jmeter之BeanShell取出需要参数,传递给下个请求

一、事件背景&#xff1a; 上周同事用Jmeter录制脚本&#xff0c;录制成功回放后&#xff0c;并没有达到自己想要的结果。 他的真实需求是&#xff0c;想从数据库取出某个字段值&#xff0c;然后对数据库做操作。 也就是想实现做参数传递的效果&#xff0c;我心痒痒的&#…

ConcurrentHashMap底层源码解析

ConcurrentHashMap线程安全&#xff0c;底层数组链表红黑树。 思想&#xff0c;分而治之。 JDK7有Segment对象&#xff0c;分段锁 JDK8没有了这个对象 总结&#xff0c; 首先计算hash&#xff0c; 如果集合没有元素&#xff0c;开始initTable方法初始化&#xff0c;这里扩容讲…

有人说ChatGPT信息不新?

Hello ,我是小索奇&#xff0c;今天给大家分享一个插件&#xff0c;这个插件可以通过抓取网页获得最新内容&#xff0c;它可以有效的避免ChatGPT信息过时&#xff0c;获取不到最新的信息等等 演示-这里问它一些问题&#xff1a; 现在几点了呀 可以看到时间也是很准确的&#x…

Linux权限(+Linux基本指令(下))

目录 一.基本指令补充 1.date指令 2.find指令 3.tar指令 4.Linux下的常用热键 二.Linux权限 1.Shell 2.Linux权限的概念 一.基本指令补充 1.date指令 &#x1f606;date指令可以用于显示日期和时间戳&#x1f606;Linux的时间戳与Unix时间戳一致,指的是从1970年1月1日…

使用无标注的数据训练Bert

文章目录 1、准备用于训练的数据集2、处理数据集3、克隆代码4、运行代码5、将ckpt模型转为bin模型使其可在pytorch中运用 Bert官方仓库&#xff1a;https://github.com/google-research/bert 1、准备用于训练的数据集 此处准备的是BBC news的数据集&#xff0c;下载链接&…

Python | 人脸识别系统 — UI界面设计

本博客为人脸识别系统的UI界面设计代码解释 人脸识别系统博客汇总&#xff1a;人脸识别系统-博客索引 项目GitHub地址&#xff1a;【待】 注意&#xff1a;阅读本博客前请先参考以下博客 工具安装、环境配置&#xff1a;人脸识别系统-简介 阅读完本博客后可以继续阅读&#xff…

不用下载就能使用的4款轻量在线PS工具

PS是一种非常熟悉的设计工具&#xff0c;也是一种在设计领域占有重要地位的软件&#xff0c;如常见的产品设计、平面设计或摄影后期设计&#xff0c;几乎与PS的使用密不可分。PS本身也有很多功能&#xff0c;每个人的日常设计图纸、图纸修复等工作都可以用PS完成。 但PS有很多…

yolov8 OpenCV DNN 部署 推理报错

yolov8是yolov5作者发布的新作品 目录 1、下载源码 2、下载权重 3、配置环境 4、导出onnx格式 5、OpenCV DNN 推理 1、下载源码 git clone https://github.com/ultralytics/ultralytics.git 2、下载权重 git clone https://github.com/ultralytics/assets/releases/dow…

MySQL知识学习05(InnoDB存储引擎对MVCC的实现)

1、一致性非锁定读和锁定读 一致性非锁定读 对于 一致性非锁定读&#xff08;Consistent Nonlocking Reads&#xff09; &#xff0c;通常做法是加一个版本号或者时间戳字段&#xff0c;在更新数据的同时版本号 1 或者更新时间戳。查询时&#xff0c;将当前可见的版本号与对…

K8S资源-configmap创建六种方式

云原生实现配置分离重要实现方式 两者都是用来存储配置文件&#xff0c;configmap存储通用的配置文件&#xff0c;secret存储需要加密的配置文件。 将配置文件configmap挂在到pod上 创建configmap 1.基于配置文件目录创建configmap kubectl create cm cmdir --from-fileconf…

医学图像分割之U-Net

一、背景及问题 在过去两年中&#xff0c;在很多视觉识别任务重&#xff0c;深度卷积网络的表现优于当时最先进的方法。但这些深度卷积网络的发展受限于网络模型的大小以及训练数据集的规模。虽然这个限制有过突破&#xff0c;也是在更深的网络、更大的数据集中产生的更好的性能…

【redis】redis的缓存过期淘汰策略

【redis】redis的缓存过期淘汰策略 文章目录 【redis】redis的缓存过期淘汰策略前言一、面试题二、redis内存满了怎么办&#xff1f;1、redis默认内存是多少&#xff1f;在哪查看&#xff1f;如何修改?在conf配置文件中可以查看 修改&#xff0c;内存默认是0redis的默认内存有…

使用意图intent构建一个多活动的Android应用

安卓意图Intent是Android应用组件(Activity、Service、Broadcast Receiver)之间进行交互的一种重要方式。Intent允许启动一个活动、启动一个服务、传递广播等。Intent使应用能够响应系统及其他应用的动作。Intent使用的主要目的有: 1、 启动Activity:可以启动自己应用内的Activ…

DDPM--生成扩散模型

DDPM–生成扩散模型 Github: https://github.com/daiyizheng/Deep-Learning-Ai/blob/master/AIGC/Diffusion.ipynb DDPM 是当前扩散模型的起点。在本文中&#xff0c;作者建议使用马尔可夫链模型&#xff0c;逐步向图像添加噪声。 函数 q ( x t ∣ x t − 1 ) q(x_t | x_t-1…

java获取真实ip的方法

在网络中&#xff0c;如果不想被人监听&#xff0c;那么就需要获取 IP地址了&#xff0c;在电脑中我们可以使用到 ip地址获取工具&#xff0c;那么如何在 Java中获取真实的 IP地址呢&#xff1f; 1、首先我们需要先准备一台电脑&#xff0c;然后将电脑进行联网&#xff1b; 2、…

ChatGPT带你一起了解C语言中的fseek()

fseek函数用于将文件指针移动到指定位置。它的原型如下&#xff1a; c int fseek(FILE *stream, long offset, int whence); 其中&#xff0c;stream是文件指针&#xff0c;offset是偏移量&#xff0c;whence是起始位置。 偏移量offset可以是正数、负数或零。 如果是正数&a…

Java --- springboot2数据响应与内容协商

目录 一、数据响应与内容协商 1.1、响应json 1.1.1、返回值解析器 1.1.2、springMVC支持的返回值类型 1.1.3、HttpMessageConverter原理 1.2、内容协商 1.2.1、引入依赖 1.2.2、 postman分别测试返回json和xml 1.2.3、开启浏览器参数方式内容协商功能 1.3、自定义 Message…

持续测试:DevOps时代质量保证的关键

在 DevOps 时代&#xff0c;持续测试已成为质量保证的一个重要方面。近年来&#xff0c;软件开发方法论发生了快速转变。随着 DevOps 的出现&#xff0c;已经发生了向自动化和持续集成与交付 (CI/CD) 的重大转变。传统的质量保证方法已不足以满足现代软件开发实践的需求。持续测…

Java——二叉树的深度

题目链接 牛客网在线oj题——二叉树的深度 题目描述 输入一棵二叉树&#xff0c;求该树的深度。从根结点到叶结点依次经过的结点&#xff08;含根、叶结点&#xff09;形成树的一条路径&#xff0c;最长路径的长度为树的深度&#xff0c;根节点的深度视为 1 。 数据范围&am…

记一次产线打印json导致的redis连接超时

服务在中午十一点上线后&#xff0c;服务每分钟发出三到四次redis连接超时告警。错误信息为&#xff1a; Dial err:dial tcp: lookup xxxxx: i/o timeout 排查过程 先是检查redis机器的情况&#xff0c;redis写入并发数较大&#xff0c;缓存中保留了一小时大概400w条数据。red…