YOLOv1代码复现1:辅助功能实现

news2024/11/25 16:49:10

YOLOv1代码复现1:辅助功能实现

前言

​ 在经历了Faster-RCNN代码解读的摧残后,下决心要搞点简单的,于是便有了本系列的博客。如果你苦于没有博客详细告诉你如何自己去实现YOLOv1,那么可以看看本系列的博客,也许可以帮助你。

​ 另外,当完成所有代码后,会将代码放在GitHub上。

目标

​ 最主要的目标肯定是能够跑通整个代码,并且我希望可以详细的告诉大家如何参考博客自己去实现,因此,文章也会记录我自己遇到的错误和调试过程。

本系列计划完成的内容与已完成的内容:

​ 本系列计划六篇,如下:

  • 第一篇:辅助功能实现(本文)
  • 第二篇:数据加载器构建(等待完成)
  • 第三篇:网络框架构建(等待完成)
  • 第四篇:损失函数构建(等待完成)
  • 第五篇:预测函数构建(等待完成)
  • 第六篇:总结(等待完成)

1. 数据集下载:

​ 决定采用VOC2012的数据集,下载地址为:

http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar

​ 下载完,解压,最好把它改为我一样的文件结构:

在这里插入图片描述

​ 关于这个数据集的介绍,可以看我这篇博客,传送门。

​ 另外,大家需要在VOC2012文件夹下,建立一个名为pascal_voc_classes.json的文件,用于存储类别名称和数字的对应关系(该文件内容如下):

{
    "aeroplane": 1,
    "bicycle": 2,
    "bird": 3,
    "boat": 4,
    "bottle": 5,
    "bus": 6,
    "car": 7,
    "cat": 8,
    "chair": 9,
    "cow": 10,
    "diningtable": 11,
    "dog": 12,
    "horse": 13,
    "motorbike": 14,
    "person": 15,
    "pottedplant": 16,
    "sheep": 17,
    "sofa": 18,
    "train": 19,
    "tvmonitor": 20
}

2. 我的目录结构介绍:

​ 众所周知,拷贝别人使用的一大弊端就是需要对一些参数进行修改,特别是文件路径相关的参数。因此,这里我说明一下我的目录结构,方便大家后期自己实现或者修改代码的时候知道修改什么地方。

​ 由于我的很多程序都要用到数据文件夹,因此我把数据文件夹单独存放了的,而没有放在我的yolov1项目下。我的目录结构如下:

根目录
	data ---- 存放数据集的文件夹
		VOC2012	---- VOC2012数据集文件夹
			Annotations
			ImageSets
			JPEGImages
			SegmentationClass
			SegmentationObject
	YOLOv1-pytorch ---- 项目文件夹
		network_file ---- 存放网络架构的文件夹
		utils ---- 存放辅助功能的文件夹

3. 画图函数的实现:

​ 我们首先需要实现的功能:能够根据已给出的信息在图像画出框和类别信息。具体效果如下:

在这里插入图片描述

​ 这个函数还是很好实现的,因为边界框的坐标信息、类别信息、概率值都是已知的,我们要做的就是把它用上。

​ 实现画图的python库,主要有两个,一是cv库,二是PIL库。这里先用PIL库来实现吧。

3.1 导入所需的库:

​ 这里主要用的是PIL库的Image、ImageDraw、ImageFont、ImageColor几个方法,因此导入:

from PIL.Image import Image
import PIL.ImageDraw as ImageDraw
import PIL.ImageFont as ImageFont
from PIL import ImageColor
import numpy as np

​ 在实现函数之前,定义一个全局变量STANDARD_COLORS,用于存放那些标准的颜色值信息,到时候需要用颜色,直接从里面随机抽取一个即可:

# 标准颜色
STANDARD_COLORS = [
    'AliceBlue', 'Chartreuse', 'Aqua', 'Aquamarine', 'Azure', 'Beige', 'Bisque',
    'BlanchedAlmond', 'BlueViolet', 'BurlyWood', 'CadetBlue', 'AntiqueWhite',
    'Chocolate', 'Coral', 'CornflowerBlue', 'Cornsilk', 'Crimson', 'Cyan',
    'DarkCyan', 'DarkGoldenRod', 'DarkGrey', 'DarkKhaki', 'DarkOrange',
    'DarkOrchid', 'DarkSalmon', 'DarkSeaGreen', 'DarkTurquoise', 'DarkViolet',
    'DeepPink', 'DeepSkyBlue', 'DodgerBlue', 'FireBrick', 'FloralWhite',
    'ForestGreen', 'Fuchsia', 'Gainsboro', 'GhostWhite', 'Gold', 'GoldenRod',
    'Salmon', 'Tan', 'HoneyDew', 'HotPink', 'IndianRed', 'Ivory', 'Khaki',
    'Lavender', 'LavenderBlush', 'LawnGreen', 'LemonChiffon', 'LightBlue',
    'LightCoral', 'LightCyan', 'LightGoldenRodYellow', 'LightGray', 'LightGrey',
    'LightGreen', 'LightPink', 'LightSalmon', 'LightSeaGreen', 'LightSkyBlue',
    'LightSlateGray', 'LightSlateGrey', 'LightSteelBlue', 'LightYellow', 'Lime',
    'LimeGreen', 'Linen', 'Magenta', 'MediumAquaMarine', 'MediumOrchid',
    'MediumPurple', 'MediumSeaGreen', 'MediumSlateBlue', 'MediumSpringGreen',
    'MediumTurquoise', 'MediumVioletRed', 'MintCream', 'MistyRose', 'Moccasin',
    'NavajoWhite', 'OldLace', 'Olive', 'OliveDrab', 'Orange', 'OrangeRed',
    'Orchid', 'PaleGoldenRod', 'PaleGreen', 'PaleTurquoise', 'PaleVioletRed',
    'PapayaWhip', 'PeachPuff', 'Peru', 'Pink', 'Plum', 'PowderBlue', 'Purple',
    'Red', 'RosyBrown', 'RoyalBlue', 'SaddleBrown', 'Green', 'SandyBrown',
    'SeaGreen', 'SeaShell', 'Sienna', 'Silver', 'SkyBlue', 'SlateBlue',
    'SlateGray', 'SlateGrey', 'Snow', 'SpringGreen', 'SteelBlue', 'GreenYellow',
    'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'White',
    'WhiteSmoke', 'Yellow', 'YellowGreen'
]

3.2 draw_obj函数实现:

​ 该函数的作用:将目标边界框信息,类别信息绘制在图片上

参数讲解:

​ 传入的参数:

参数意义
image需要绘制的图片
boxes目标边界框信息
格式为:[[left, top, right, bottom],…]
classes目标类别信息
scores目标概率信息
category_index类别与名称字典
box_thresh过滤的概率阈值
line_thickness边界框宽度
font字体类型
font_size字体大小
draw_boxes_on_image是否将边界框画在图像上
默认为True

​ 这里,需要说明一下其中的几个关键参数,方便大家理解:

  • boxes、classes、scores

​ 这三个参数分布是一张图片的所有边界框、类别、概率值,因为一张图片可能并不只有一个对象,因此这三个参数的值都是类似[[...],[...],......]这样列表里面嵌套一个列表的形式。

  • category_index

​ 因为我们知道,计算机是没有办法直接识别诸如person/bike/car这样的自然语言的,因此需要把这些类别值转为数字值,就像上面创建的json文件一样,这里使用一个字典来存储相关值。

  • box_thresh

​ 这个参数的作用就是过滤掉那些低概率的值,如果一个框预测概率过低,其实画出来也没啥意义,因此需要进行过滤,不过如果你不想过滤,直接设置为0即可。

返回值:

​ 返回的是PIL Image对象,这个对象可以自带矩形边界框、类别文字等信息,然后可以使用matplotlib中的方法将图像显示出来。

函数实现:

​ 首先,需要对现有的值进行过滤,过滤方法采用numpy.greater方法,其返回过滤后剩下的值的索引,然后利用这个索引去筛选值:

# 过滤掉低概率的目标
idxs = np.greater(scores, box_thresh)
# 需要同时处理boxes、classes、scores、masks
boxes = boxes[idxs]
classes = classes[idxs]
scores = scores[idxs]

​ 如果全部过滤掉了,那么直接结束该方法:

# 如果boxes长度为0,表示所有的框都过滤了,就不需要画了
if len(boxes) == 0:
    return image

​ 接着,根据类别个数,从标注颜色列表中抽取对应颜色,只是获取的颜色需要用ImageColor,getrgb方法获取rgb值:

# 从定义的颜色列表中抽取颜色
# ImageColor.getrgb 获取颜色的rgb值
colors = [ImageColor.getrgb(STANDARD_COLORS[cls % len(STANDARD_COLORS)]) for cls in classes]

​ 判断是否需要画边界框,并用ImageDraw.Draw创建绘制对象:

# 如果需要画边界框
if draw_boxes_on_image:
    # 创建画图对象
    draw = ImageDraw.Draw(image)

​ 然后,由于一张图对象可能不只一个,因此需要遍历boxes等参数,进行画图:

# 如果需要画边界框
if draw_boxes_on_image:
    # 创建画图对象
    draw = ImageDraw.Draw(image)
    # 开始迭代绘图
    for box, cls, score, color in zip(boxes, classes, scores, colors):
        # 边界框的坐标:左上角x、y + 右下角x、y
        left, top, right, bottom = box
        # 绘制目标边界框,逆时针画图
        draw.line([(left, top), (left, bottom), (right, bottom),
                   (right, top), (left, top)], width=line_thickness, fill=color)
        # 绘制类别和概率信息
        draw_text(draw, box.tolist(), int(cls), float(score), category_index, color, font, font_size)

​ 其中,填写文字的函数,我们单独实现。

3.3 draw_text函数实现:

​ 该函数的作用:在draw_obj函数的基础上绘制图像上的文字。

参数讲解:

​ 参数种类和draw_obj函数相差不大,只是注意该函数的参数不像draw_obj函数一样,是多个值了(之前boxes参数为[[....],[....],...]),即一个参数仅有一个对象的值(这里box参数为[.....])。

​ 另外,draw_text第一个参数,是已经绘制了矩形的图像对象,所以接下来的任务就是在该对象的基础上绘制文字内容。

返回值:

​ 返回一个完整的绘制完成的图像对象。

实现:

​ 利用之前导入的ImageFont方法创建字体对象,当然,如果你的电脑没有指定的字体,那么就使用默认字体:

# 创建字体对象,如果创建失败(比如目前指定的字体你没有),就使用默认的字体
try:
    font = ImageFont.truetype(font, font_size)
except IOError:
    font = ImageFont.load_default()

​ 接着,获取box的坐标、并将1、2这样的数字信息转为如preson\bike的类别信息,并将形如0.2、0.3的概率值转为百分数形式:

# 获取坐标
left, top, right, bottom = box
# 将数字的类别转为真实的类别信息,并加上概率值构成“ person 99% ”这样的字符串
display_str = f"{category_index[str(cls)]}: {int(100 * score)}%"

​ 然后,需要设置字体的高度,使用font.getsize方法可以获取输入字符的大小,那么将要填写的一句话全输入给该方法,然后获取所有字符的最大值即可:

# 设置字体的高度
# font.getsize获取输入文字中每个字符的大小
display_str_heights = [font.getsize(ds)[1] for ds in display_str]
display_str_height = (1 + 2 * 0.05) * max(display_str_heights)

接下来是一个难点:文字需要设置它的坐标。这里,看下图分析坐标获取思路:

在这里插入图片描述

​ 上图是正常的情况,即边界框最上面的坐标与文字高度不重叠,意味着可以正常放下文字内容。但是也有不正常的情况:

在这里插入图片描述

​ 如上图所示,填写的文字没有办法放在边界框的上面,此时需要进行一定的修正,采取的方法是把文字放入边界框内容

​ 基于此,可以编写代码:

# 如果文字的高度没有超过图像最高点
if top > display_str_height:
    # 设置文字的坐标
    # 这里减法, 注意坐标轴的朝向
    text_top = top - display_str_height
    text_bottom = top
else:
    # 如果超过了,就设置文字的坐标为边界框的下面
    text_top = bottom
    text_bottom = bottom + display_str_height

​ 最后,就是绘制文字了。这里,一个字符一个字符的绘制,是因为需要为每个字符添加一个矩形框背景,这样不同字符组合在一起,将变为一个大的矩形框了:

# 开始画
# 一个字符一个字符的画
for ds in display_str:
    # 获取文字的宽和高
    text_width, text_height = font.getsize(ds)
    # 设置每个字符的偏离距离
    margin = np.ceil(0.05 * text_width)
    # 画一个矩形
    draw.rectangle([(left, text_top),
                    (left + text_width + 2 * margin, text_bottom)], fill=color)
    # 画文字
    draw.text((left + margin, text_top),
              ds,
              fill='black',
              font=font)
    left += text_width

4. 效果展示:

​ 由于我们还没有实现其它的代码,所以这里用一个简单的方法来检测代码是否可以正确运行。

​ 首先,需要导入之前没有导入过的库:

# 导入调试需要的库
from matplotlib import pyplot as plt
import json
from PIL import Image

​ 接着,需要读取我们的json文件,并转为字典:

# 读取类别json文件,并转为字典值
category_index = {}
try:
    # 路劲需要改为自己的
    json_file = open('../../data/VOC2012/pascal_voc_classes.json', 'r')
    class_dict = json.load(json_file)
    category_index = {str(v): str(k) for k, v in class_dict.items()}
except Exception as e:
    print(e)
    exit(-1)

​ 这个字典的值形如下表,即为类别和数字值的对应关系:

{
	'person' : 15
	'bike' : 2
}

​ 接着,我们可以调用我们的函数了,不过我们只能手动传入对应参数了:

# 开始绘制
# 打开一张图片,需要修改为自己的路径
img = Image.open('../../data/VOC2012/JPEGImages/2007_000027.jpg')
plot_img = draw_objs(img,
                     np.array([[174,101,349,351]]), # 传入该图像对应的注解信息
                     np.array([15]),
                     np.array([1]),
                     category_index=category_index,
                     box_thresh=0.5,
                     line_thickness=3,
                     font='arial.ttf',
                     font_size=20)
plt.imshow(plot_img)
plt.show()

​ 说明一下,上面的参数值都是如何填写的:先任意选择一张图片,然后找到其对应的注解文件;接着,任意选择一个对象,找到其类别名称和坐标值即可。由于我们采用的真实图像的值,即类别概率肯定为1.

在这里插入图片描述

从上面传入的参数,也可以直观的理解各个参数的含义和形式。

​ 运行上述代码结果如下:

在这里插入图片描述

5. 完整代码:

# author: baiCai
# 本文件拷贝别人的,用于画框图,删除了一些自己用不到的内容
from PIL.Image import Image
import PIL.ImageDraw as ImageDraw
import PIL.ImageFont as ImageFont
from PIL import ImageColor
import numpy as np

# 标准颜色
STANDARD_COLORS = [
    'AliceBlue', 'Chartreuse', 'Aqua', 'Aquamarine', 'Azure', 'Beige', 'Bisque',
    'BlanchedAlmond', 'BlueViolet', 'BurlyWood', 'CadetBlue', 'AntiqueWhite',
    'Chocolate', 'Coral', 'CornflowerBlue', 'Cornsilk', 'Crimson', 'Cyan',
    'DarkCyan', 'DarkGoldenRod', 'DarkGrey', 'DarkKhaki', 'DarkOrange',
    'DarkOrchid', 'DarkSalmon', 'DarkSeaGreen', 'DarkTurquoise', 'DarkViolet',
    'DeepPink', 'DeepSkyBlue', 'DodgerBlue', 'FireBrick', 'FloralWhite',
    'ForestGreen', 'Fuchsia', 'Gainsboro', 'GhostWhite', 'Gold', 'GoldenRod',
    'Salmon', 'Tan', 'HoneyDew', 'HotPink', 'IndianRed', 'Ivory', 'Khaki',
    'Lavender', 'LavenderBlush', 'LawnGreen', 'LemonChiffon', 'LightBlue',
    'LightCoral', 'LightCyan', 'LightGoldenRodYellow', 'LightGray', 'LightGrey',
    'LightGreen', 'LightPink', 'LightSalmon', 'LightSeaGreen', 'LightSkyBlue',
    'LightSlateGray', 'LightSlateGrey', 'LightSteelBlue', 'LightYellow', 'Lime',
    'LimeGreen', 'Linen', 'Magenta', 'MediumAquaMarine', 'MediumOrchid',
    'MediumPurple', 'MediumSeaGreen', 'MediumSlateBlue', 'MediumSpringGreen',
    'MediumTurquoise', 'MediumVioletRed', 'MintCream', 'MistyRose', 'Moccasin',
    'NavajoWhite', 'OldLace', 'Olive', 'OliveDrab', 'Orange', 'OrangeRed',
    'Orchid', 'PaleGoldenRod', 'PaleGreen', 'PaleTurquoise', 'PaleVioletRed',
    'PapayaWhip', 'PeachPuff', 'Peru', 'Pink', 'Plum', 'PowderBlue', 'Purple',
    'Red', 'RosyBrown', 'RoyalBlue', 'SaddleBrown', 'Green', 'SandyBrown',
    'SeaGreen', 'SeaShell', 'Sienna', 'Silver', 'SkyBlue', 'SlateBlue',
    'SlateGray', 'SlateGrey', 'Snow', 'SpringGreen', 'SteelBlue', 'GreenYellow',
    'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'White',
    'WhiteSmoke', 'Yellow', 'YellowGreen'
]


def draw_text(draw,box,cls,score,category_index,color,font='arial.ttf',font_size=24):
    """
    将目标边界框和类别信息绘制到图片上
    参数:
        draw : 画图对象,可以使用画直线等等方法
        box : 一个边界框,里面有坐标信息
        cls : 对象的类别,为int值,需要使用category_index转为字符串值
        score : 对象的类别概率值
        category_index : 不同的索引对应的类别信息
        color : 使用的颜色
        font :字体
        font_size : 字大小
    """
    # 创建字体对象,如果创建失败(比如目前指定的字体你没有),就使用默认的字体
    try:
        font = ImageFont.truetype(font, font_size)
    except IOError:
        font = ImageFont.load_default()

    # 获取坐标
    left, top, right, bottom = box
    # 将数字的类别转为真实的类别信息,并加上概率值构成“ person 99% ”这样的字符串
    display_str = f"{category_index[str(cls)]}: {int(100 * score)}%"
    # 设置字体的高度
    # font.getsize获取输入文字中每个字符的大小
    display_str_heights = [font.getsize(ds)[1] for ds in display_str]
    display_str_height = (1 + 2 * 0.05) * max(display_str_heights)

    # 如果文字的高度没有超过图像最高点
    if top > display_str_height:
        # 设置文字的坐标
        # 这里减法, 注意坐标轴的朝向
        text_top = top - display_str_height
        text_bottom = top
    else:
        # 如果超过了,就设置文字的坐标为边界框的下面
        text_top = bottom
        text_bottom = bottom + display_str_height

    # 开始画
    # 一个字符一个字符的画
    for ds in display_str:
        # 获取文字的宽和高
        text_width, text_height = font.getsize(ds)
        # 设置每个字符的偏离距离
        margin = np.ceil(0.05 * text_width)
        # 画一个矩形
        draw.rectangle([(left, text_top),
                        (left + text_width + 2 * margin, text_bottom)], fill=color)
        # 画文字
        draw.text((left + margin, text_top),
                  ds,
                  fill='black',
                  font=font)
        left += text_width


def draw_objs(image,boxes=None,classes=None,scores=None,category_index=None,box_thresh=0.1,line_thickness=8,font='arial.ttf',font_size=24,draw_boxes_on_image=True):
    """
    将目标边界框信息,类别信息绘制在图片上
    Args:
        image: 需要绘制的图片
        boxes: 目标边界框信息
        classes: 目标类别信息
        scores: 目标概率信息
        category_index: 类别与名称字典
        box_thresh: 过滤的概率阈值
        line_thickness: 边界框宽度
        font: 字体类型
        font_size: 字体大小
        draw_boxes_on_image:是否将边界框画在图像上,默认为True
    Returns:

    """
    # 过滤掉低概率的目标
    idxs = np.greater(scores, box_thresh)
    # 需要同时处理boxes、classes、scores、masks
    boxes = boxes[idxs]
    classes = classes[idxs]
    scores = scores[idxs]

    # 如果boxes长度为0,表示所有的框都过滤了,就不需要画了
    if len(boxes) == 0:
        return image

    # 从定义的颜色列表中抽取颜色
    # ImageColor.getrgb 获取颜色的rgb值
    colors = [ImageColor.getrgb(STANDARD_COLORS[cls % len(STANDARD_COLORS)]) for cls in classes]

    # 如果需要画边界框
    if draw_boxes_on_image:
        # 创建画图对象
        draw = ImageDraw.Draw(image)
        # 开始迭代绘图
        for box, cls, score, color in zip(boxes, classes, scores, colors):
            # 边界框的坐标
            left, top, right, bottom = box
            # 绘制目标边界框,顺时针画图
            draw.line([(left, top), (left, bottom), (right, bottom),
                       (right, top), (left, top)], width=line_thickness, fill=color)
            # 绘制类别和概率信息
            draw_text(draw, box.tolist(), int(cls), float(score), category_index, color, font, font_size)


    return image

if __name__ == '__main__':
    # 导入调试需要的库
    from matplotlib import pyplot as plt
    import json
    from PIL import Image
    # 读取类别json文件,并转为字典值
    category_index = {}
    try:
        json_file = open('../../data/VOC2012/pascal_voc_classes.json', 'r')
        class_dict = json.load(json_file)
        category_index = {str(v): str(k) for k, v in class_dict.items()}
    except Exception as e:
        print(e)
        exit(-1)
    # 开始绘制
    # 打开一张图片,需要修改为自己的路径
    img = Image.open('../../data/VOC2012/JPEGImages/2007_000027.jpg')
    plot_img = draw_objs(img,
                         np.array([[174,101,349,351]]), # 传入该图像对应的注解信息
                         np.array([15]),
                         np.array([1]),
                         category_index=category_index,
                         box_thresh=0.5,
                         line_thickness=3,
                         font='arial.ttf',
                         font_size=20)
    plt.imshow(plot_img)
    plt.show()

6. 总结:

​ 这里,我们不仅仅实现了画图的函数,还定义了传入参数的形式,也为后期构建数据加载等文件定下了返回值的基调。所谓,千里之路始于足下,顺着思路往下写,迟早也会把YOLOv1搞定的^_^。

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

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

相关文章

操作指南|如何创建x-chain DAO

DAO是一个去中心化组织,大体与任何其他组织一样,但它是由智能合约中编码的规则所管理,并使DApps等能够完全去中心化且自主运行。 📄 查看MoonbeamDocs 这与通常的分步教程不同,该推文旨在分享关于运行去中心化自治组…

delta.io 2.3.0 overwrite模式 overwriteSchema df覆盖table的表schema

初始化一张表 overwriteSchema 默认为false 图中注意事项: Note that the schema cant be overwritten when using replaceWhere. overwriteSchema 配置为true 可成功覆盖插入 overwriteSchema=true的配置项必须配置.mode("overwrite") 而不能是.mode("append…

( 栈和队列) 225. 用队列实现栈 ——【Leetcode每日一题】

❓225. 用队列实现栈 难度:简单 请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。 实现 MyStack 类: void push(int x) 将元素 x 压入…

国云筑基“翼”气风发,天翼云以科技创新绘就数字中国蓝图

科技云报道原创。 全球新一轮技术革命方兴未艾,特别是以数字技术为核心的信息技术革命,正在实现群体突破和加快广泛深度应用。 从2017年的“促进数字经济加快成长”,到2019年的“壮大数字经济”,到2020年的“全面推进‘互联网&am…

从0搭建Vue3组件库(八):使用 release-it 实现自动管理发布组件库

使用 release-it 实现自动管理发布组件库 上一篇文章已经打包好我们的组件库了,而本篇文章将介绍如何发布一个组件库。当然本篇文章介绍的肯定不单单只是发布那么简单。 组件库发布 我们要发布的包名为打包后的 easyest,因此在 easyest 下执行pnpm init生成package.json {&…

数字中国建设2522整体框架

作为影响中国未来发展的重磅文件,《数字中国建设整体布局规划》明确了两个重要时间节点: 到 2025 年,基本形成横向打通、纵向贯通、协调有力的一体化推进格局,数字中国建设取得重要进展; 到 2035 年,数字化…

Compiler- 尾调用

我们还是用例子来引入本次要探讨的问题--尾调用 #include <stdio.h>int fib(int a) {return a < 2 ? 1 : fib(a - 1) fib(a - 2); }int main() {int n,result;scanf("%d",&n);result fib(n);printf("result is %d.\n",result);return 0; …

JavaWeb02(Servlet页面跳转方式表单提交方式)

目录 一.servlet 1.1 什么是servlet? 1.2 实现接口,初始代码 1.3 学会配置和映射 1.4 掌握servlet的生命周期 生命周期的各个阶段 1.5 获取servlet初始化参数和上下文参数 1.5.1 初始代码 推荐使用 1.5.2 初始化参数 1.5.3 上下文参数 1.6 servlet应用:处理用户登…

多处理器的汇编编程

多处理器编程&#xff0c;本质上&#xff0c;就是把MR给每个处理器复制一份 每个处理器拿到MR&#xff0c;形成了自己的缓存内存空间&#xff0c;然后再在运行期间把运算结果写入共享内存 把i做成一条指令 使用asm嵌入汇编&#xff0c;向sum的寄存器直接写入1的值 把C语言转…

【Python入门第五十三天】Python丨NumPy 中的随机数

什么是随机数&#xff1f; 随机数并不意味着每次都有不同的数字。随机意味着无法在逻辑上预测的事物。 伪随机和真随机 计算机在程序上工作&#xff0c;程序是权威的指令集。因此&#xff0c;这意味着必须有某种算法来生成随机数。 如果存在生成随机数的程序&#xff0c;则…

10个必备的建筑可视化3dmax插件

当日复一日地处理项目时&#xff0c;很容易陷入舒适但效率不高的工作流程中。 插件是在不牺牲工作质量的情况下改进和加快工作流程的好方法。 尤其是在建筑可视化时&#xff0c;快节奏的行业往往需要艺术家灵活机智。 在本文中&#xff0c;我们将介绍 10 个最好的 3ds Max 插件…

Springboot 整合 JPA 及 Swagger2

首先是官方文档&#xff1a; Spring Data JPA - Reference Documentationhttps://docs.spring.io/spring-data/jpa/docs/2.2.4.RELEASE/reference/html/#repositories.query-methods 1、JPA相关概念 2、创建 Springboot 项目 修改 pom 文件&#xff0c;可以直接进行复制粘贴&a…

百度APP iOS端包体积50M优化实践(二) 图片优化

**一、前言删除线格式 ** 在上一篇文章&#xff0c;我们介绍了包体积优化的必要性、安装包组成部分和生成过程、国内外大厂APP包体积分析、百度APP包体积优化技术方案及各项收益&#xff0c;本文重点讲述图片优化&#xff0c;解压IPA包后发现&#xff0c;百度APP中asset和bund…

Seurat -- Normalize Data

brief seurat提供的教学里面包含了Standard pre-processing workflow,workflow包括QC&#xff0c;normalization&#xff0c;scale data &#xff0c;detection of highly variable features。其中 normalization就有蛮多方法的&#xff0c;seurat自己就提供了两种&#xff0c…

ChatGpt接入Word文档,让你秒变职场达人!

今天跟大家分享下我们如何使用VBA代码&#xff0c;将ChatGpt接入Word文档&#xff0c;操作非常的简单&#xff0c;但是开始之前我们需要做2项准备 1. 获取ChatGpt的API 2. 魔法上网 准备好这2件事后&#xff0c;我们就可以着手制作了: 一&#xff0c;设置代码 二&…

微软的“牛头怪时刻”

2014年&#xff0c;当萨提亚纳德拉接任微软CEO时&#xff0c;他面对的是一家停滞且难以在快速发展的技术领域保持竞争优势的公司。自那以后&#xff0c;纳德拉将其重点从传统操作系统和生产力软件&#xff0c;转向云计算和人工智能&#xff0c;被认为重振了微软。​ 让我们以O…

ThreadPoolExecutor源码阅读流程图

1.创建线程池 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), def…

shell脚本编程规范与变量

目录 一.shell脚本的概述2.1 shell的作用 三. shell脚本的作用3.1 编写第一个shell脚本3.1.1 Shell 脚本的构成&#xff1a;3.1.2 脚本的执行方式 三. 重定向与管道符操作3.2 重定向操作3.2 管道操作符号 四. shell的变量的作用&#xff0c;类型4.1 定义变量4.2 命名的规则4.3 …

辛弃疾最有代表性的十首词

辛弃疾的词&#xff0c;风格多样&#xff0c;题材广阔&#xff0c;几乎涉及到生活中的各个方面&#xff0c;从爱国情怀到日常生活&#xff0c;甚至连戒酒这种事都能写入词中。辛弃疾也是两宋词人中&#xff0c;存词最多的作家之一&#xff0c;现存的六百多首作品。 辛弃疾的词…

【数据结构:线性表】单链表

在学习了顺序表&#xff0c;我们可能会对其有一些思考&#xff1a; 中间/头部的插入删除&#xff0c;时间复杂度为O(N)增容需要申请新空间&#xff0c;拷贝数据&#xff0c;释放旧空间。会有不小的消耗。增容一般是呈2倍的增长&#xff0c;势必会有一定的空间浪费。例如当前容…