第P10周-Pytorch实现车牌号识别

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊

目标

具体实现

(一)环境

语言环境:Python 3.10
编 译 器: PyCharm
框 架: Pytorch

(二)具体步骤
1. 文件结构

image.png

2. config.py
import argparse  
  
def get_options(parser=argparse.ArgumentParser()):  
    parser.add_argument('--workers', type=int, default=0, help='Number of parallel workers')  
    parser.add_argument('--batch-size', type=int, default=4, help='input batch size, default=32')  
    parser.add_argument('--size', type=tuple, default=(224, 224), help='input image size')  
    parser.add_argument('--lr', type=float, default=1e-4, help='learning rate, default=0.0001')  
    parser.add_argument('--epochs', type=int, default=20, help='number of epochs')  
    parser.add_argument('--seed', type=int, default=112, help='random seed')  
    parser.add_argument('--save-path', type=str, default='./models/', help='path to save checkpoints')  
  
    opt = parser.parse_args()  
  
    if opt:  
        print(f'num_workers:{opt.workers}')  
        print(f'batch_size:{opt.batch_size}')  
        print(f'learn rate:{opt.lr}')  
        print(f'epochs:{opt.epochs}')  
        print(f'random seed:{opt.seed}')  
        print(f'save_path:{opt.save_path}')  
  
    return opt  
  
if __name__ == '__main__':  
    opt = get_options()
3. Utils.py
import torch  
import pathlib  
import matplotlib.pyplot as plt  
from torchvision.transforms import transforms  
  
  
# 第一步:设置GPU  
def USE_GPU():  
    if torch.cuda.is_available():  
        print('CUDA is available, will use GPU')  
        device = torch.device("cuda")  
    else:  
        print('CUDA is not available. Will use CPU')  
        device = torch.device("cpu")  
    return device  
  
temp_dict = dict()  
def recursive_iterate(path):  
    """  
    根据所提供的路径遍历该路径下的所有子目录,列出所有子目录下的文件  
    :param path: 路径  
    :return: 返回最后一级目录的数据  
    """    path = pathlib.Path(path)  
    for file in path.iterdir():  
        if file.is_file():  
            temp_key = str(file).split('\\')[-2]  
            if temp_key in temp_dict:  
                temp_dict.update({temp_key: temp_dict[temp_key] + 1})  
            else:  
                temp_dict.update({temp_key: 1})  
            # print(file)  
        elif file.is_dir():  
            recursive_iterate(file)  
  
    return temp_dict  
  
  
def data_from_directory(directory, train_dir=None, test_dir=None, show=False):  
    """  
    提供是的数据集是文件形式的,提供目录方式导入数据,简单分析数据并返回数据分类  
    :param test_dir: 是否设置了测试集目录  
    :param train_dir: 是否设置了训练集目录  
    :param directory: 数据集所在目录  
    :param show: 是否需要以柱状图形式显示数据分类情况,默认显示  
    :return: 数据分类列表,类型: list  
    """    global total_image  
    print("数据目录:{}".format(directory))  
    data_dir = pathlib.Path(directory)  
  
    # for d in data_dir.glob('**/*'): # **/*通配符可以遍历所有子目录  
    #     if d.is_dir():  
    #         print(d)    class_name = []  
    total_image = 0  
    # temp_sum = 0  
  
    if train_dir is None or test_dir is None:  
        data_path = list(data_dir.glob('*'))  
        class_name = [str(path).split('\\')[-1] for path in data_path]  
        print("数据分类: {}, 类别数量:{}".format(class_name, len(list(data_dir.glob('*')))))  
        total_image = len(list(data_dir.glob('*/*')))  
        print("图片数据总数: {}".format(total_image))  
    else:  
        temp_dict.clear()  
        train_data_path = directory + '/' + train_dir  
        train_data_info = recursive_iterate(train_data_path)  
        print("{}目录:{},{}".format(train_dir, train_data_path, train_data_info))  
  
        temp_dict.clear()  
        test_data_path = directory + '/' + test_dir  
        print("{}目录:{},{}".format(test_dir,  test_data_path, recursive_iterate(test_data_path)))  
        class_name = temp_dict.keys()  
  
    if show:  
        # 隐藏警告  
        import warnings  
        warnings.filterwarnings("ignore")  # 忽略警告信息  
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签  
        plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号  
        plt.rcParams['figure.dpi'] = 100  # 分辨率  
  
        for i in class_name:  
            data = len(list(pathlib.Path((directory + '\\' + i + '\\')).glob('*')))  
            plt.title('数据分类情况')  
            plt.grid(ls='--', alpha=0.5)  
            plt.bar(i, data)  
            plt.text(i, data, str(data), ha='center', va='bottom')  
            print("类别-{}:{}".format(i, data))  
            # temp_sum += data  
        plt.show()  
  
    # if temp_sum == total_image:  
    #     print("图片数据总数检查一致")  
    # else:    #     print("数据数据总数检查不一致,请检查数据集是否正确!")  
    return class_name  
  
  
def get_transforms_setting(size):  
    """  
    获取transforms的初始设置  
    :param size: 图片大小  
    :return: transforms.compose设置  
    """    transform_setting = {  
        'train': transforms.Compose([  
            transforms.Resize(size),  
            transforms.ToTensor(),  
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  
        ]),  
        'test': transforms.Compose([  
            transforms.Resize(size),  
            transforms.ToTensor(),  
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  
        ])  
    }  
  
    return transform_setting  
  
  
# 训练循环  
def train(dataloader, device, model, loss_fn, optimizer):  
    size = len(dataloader.dataset)  # 训练集的大小  
    num_batches = len(dataloader)  # 批次数目, (size/batch_size,向上取整)  
  
    train_loss, train_acc = 0, 0  # 初始化训练损失和正确率  
  
    for X, y in dataloader:  # 获取图片及其标签  
        X, y = X.to(device), y.to(device)  
  
        # 计算预测误差  
        pred = model(X)  # 网络输出  
        loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失  
  
        # 反向传播  
        optimizer.zero_grad()  # grad属性归零  
        loss.backward()  # 反向传播  
        optimizer.step()  # 每一步自动更新  
  
        # 记录acc与loss  
        train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()  
        train_loss += loss.item()  
  
    train_acc /= size  
    train_loss /= num_batches  
  
    return train_acc, train_loss  
  
  
def test(dataloader, device, model, loss_fn):  
    size = len(dataloader.dataset)  # 测试集的大小  
    num_batches = len(dataloader)  # 批次数目, (size/batch_size,向上取整)  
    test_loss, test_acc = 0, 0  
  
    # 当不进行训练时,停止梯度更新,节省计算内存消耗  
    with torch.no_grad():  
        for imgs, target in dataloader:  
            imgs, target = imgs.to(device), target.to(device)  
  
            # 计算loss  
            target_pred = model(imgs)  
            loss = loss_fn(target_pred, target)  
  
            test_loss += loss.item()  
            test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()  
  
    test_acc /= size  
    test_loss /= num_batches  
  
    return test_acc, test_loss  
  
  
from PIL import Image  
  
def predict_one_image(image_path, device, model, transform, classes):  
    """  
    预测单张图片  
    :param image_path: 图片路径  
    :param device: CPU or GPU    :param model: cnn模型  
    :param transform:    :param classes:    :return:  
    """    test_img = Image.open(image_path).convert('RGB')  
    plt.imshow(test_img)  # 展示预测的图片  
  
    test_img = transform(test_img)  
    img = test_img.to(device).unsqueeze(0)  
  
    model.eval()  
    output = model(img)  
  
    _, pred = torch.max(output, 1)  
    pred_class = classes[pred]  
    print(f'预测结果是:{pred_class}')
4.dataset.py
import os  
  
import torch  
from PIL import Image  
from torch.utils.data import Dataset  
from torchvision import transforms, datasets  
from Utils import get_transforms_setting  
  
class CarLicenceDataset(Dataset):  
    def __init__(self, root_dir, all_labels, transform=None):  
        self.img_dir = root_dir    # 图像目录路径  
        self.img_labels = all_labels # 获取标签信息  
        self.transform = transform  # 目标转换函数  
  
        # self.total_data = datasets.ImageFolder(root_dir, transform=transform)  
        # print(self.total_data)        # # 划分数据集  
        # train_size = int(0.8 * len(self.total_data))  
        # test_size = len(self.total_data) - train_size        # self.train_dataset, self.test_dataset = torch.utils.data.random_split(self.total_data, [train_size, test_size])        # print(self.train_dataset, self.test_dataset)  
    def __len__(self):  
        return len(self.img_labels)  
  
    def __getitem__(self, idx):  
        image = Image.open(self.img_dir[idx]).convert('RGB')  
        label = self.img_labels[idx]  
  
        if self.transform:  
            image = self.transform(image)  
  
        return image, label  
  
    # def __getds__(self, dstype):  
    #     if dstype == 'train':    #         return self.train_dataset    #     elif dstype == 'test':    #         return self.test_dataset    #     else:    #         pass
5.model.py
import torch  
from torch import nn  
import torch.nn.functional as F  
  
class Network_bn(nn.Module):  
    def __init__(self, label_name_len=1, char_set_len=1):  
        super(Network_bn, self).__init__()  
        """  
        nn.Conv2d()函数:  
        第一个参数(in_channels)是输入的channel数量  
        第二个参数(out_channels)是输出的channel数量  
        第三个参数(kernel_size)是卷积核大小  
        第四个参数(stride)是步长,默认为1  
        第五个参数(padding)是填充大小,默认为0  
        """        self.conv1 = nn.Conv2d(in_channels=3, out_channels=12, kernel_size=5, stride=1, padding=0)  
        self.bn1 = nn.BatchNorm2d(12)  
        self.conv2 = nn.Conv2d(in_channels=12, out_channels=12, kernel_size=5, stride=1, padding=0)  
        self.bn2 = nn.BatchNorm2d(12)  
        self.pool = nn.MaxPool2d(2 ,2)  
        self.conv4 = nn.Conv2d(in_channels=12, out_channels=24, kernel_size=5, stride=1, padding=0)  
        self.bn4 = nn.BatchNorm2d(24)  
        self.conv5 = nn.Conv2d(in_channels=24, out_channels=24, kernel_size=5, stride=1, padding=0)  
        self.bn5 = nn.BatchNorm2d(24)  
        self.fc1 = nn.Linear(24 *50 *50, label_name_len * char_set_len)  
        self.reshape = Reshape([label_name_len ,char_set_len])  
  
    def forward(self, x):  
        x = F.relu(self.bn1(self.conv1(x)))  
        x = F.relu(self.bn2(self.conv2(x)))  
        x = self.pool(x)  
        x = F.relu(self.bn4(self.conv4(x)))  
        x = F.relu(self.bn5(self.conv5(x)))  
        x = self.pool(x)  
        x = x.view(-1, 24 *50 *50)  
        x = self.fc1(x)  
  
        # 最终reshape  
        x = self.reshape(x)  
  
        return x  
  
# 定义Reshape层  
class Reshape(nn.Module):  
    def __init__(self, shape):  
        super(Reshape, self).__init__()  
        self.shape = shape  
  
    def forward(self, x):  
        return x.view(x.size(0), *self.shape)  
  
device = "cuda" if torch.cuda.is_available() else "cpu"  
print("Using {} device".format(device))  
  
model = Network_bn().to(device)  
model
6. train.py
import torch  
import os,PIL,random,pathlib  
import matplotlib.pyplot as plt  
from torch import nn  
import numpy as np  
import torchsummary  
  
from dataset import CarLicenceDataset  
from Utils import USE_GPU, get_transforms_setting  
from config import get_options  
from model import Network_bn  
  
  
device = USE_GPU()  # 获取GPU,有则使用GPU,否则使用CPU
opt = get_options()  # 获取训练超参数,预设的
transform = get_transforms_setting((224, 224))  # 获取数据转换配置
  
  
# 支持中文  
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签  
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号  
  
data_dir = './data/licence_plate/'  # 数据集路径
data_dir = pathlib.Path(data_dir)  
  
data_paths  = list(data_dir.glob('*'))  
classeNames = [str(path).split("\\")[2].split("_")[1].split(".")[0] for path in data_paths]  
# print(classeNames)  
  
data_paths     = list(data_dir.glob('*'))  
data_paths_str = [str(path) for path in data_paths]  
# print(data_paths_str)  
  
plt.figure(figsize=(14, 5))  
plt.suptitle("数据示例", fontsize=15)  
  
for i in range(18):  
    plt.subplot(3, 6, i + 1)  
    # plt.xticks([])  
    # plt.yticks([])    # plt.grid(False)  
    # 显示图片  
    images = plt.imread(data_paths_str[i])  
    plt.imshow(images)  
  
plt.show()  

image.png


  
char_enum = ["京","沪","津","渝","冀","晋","蒙","辽","吉","黑","苏","浙","皖","闽","赣","鲁",\  
              "豫","鄂","湘","粤","桂","琼","川","贵","云","藏","陕","甘","青","宁","新","军","使"]  
  
number   = [str(i) for i in range(0, 10)]    # 0 到 9 的数字  
alphabet = [chr(i) for i in range(65, 91)]   # A 到 Z 的字母  
  
char_set       = char_enum + number + alphabet  
char_set_len   = len(char_set)  
label_name_len = len(classeNames[0])  
  
# 将字符串数字化  
def text2vec(text):  
    vector = np.zeros([label_name_len, char_set_len])  
    for i, c in enumerate(text):  
        idx = char_set.index(c)  
        vector[i][idx] = 1.0  
    return vector  
  
all_labels = [text2vec(i) for i in classeNames]  
  
total_data = CarLicenceDataset(data_paths_str, all_labels, transform['train'])  
print(total_data)  
  
train_size = int(0.8 * len(total_data))  
test_size  = len(total_data) - train_size  
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])  
print(train_size,test_size)  
  
train_loader = torch.utils.data.DataLoader(train_dataset,  
                                           batch_size=16,  
                                           shuffle=True)  
test_loader = torch.utils.data.DataLoader(test_dataset,  
                                          batch_size=16,  
                                          shuffle=True)  
  
print("The number of images in a training set is: ", len(train_loader)*16)  
print("The number of images in a test set is: ", len(test_loader)*16)  
print("The number of batches per epoch is: ", len(train_loader))  
  
for X, y in test_loader:  
    print("Shape of X [N, C, H, W]: ", X.shape)  
    print("Shape of y: ", y.shape, y.dtype)  
    break  
  
model = Network_bn(label_name_len, char_set_len).to(device)

torchsummary.summary(model, input_size=(3, 224, 224))  # 打印网络结构

image.png

# 创建一个Adam优化器
optimizer  = torch.optim.Adam(model.parameters(),  
                              lr=opt.learning_rate,  # 从配置文件中取
                              weight_decay=0.0001)  
  
loss_model = nn.CrossEntropyLoss()  # 创建一个交叉熵损失函数
  
from torch.autograd import Variable  
  
  
def test(model, test_loader, loss_model):  
    size = len(test_loader.dataset)  
    num_batches = len(test_loader)  
  
    model.eval()  
    test_loss, correct = 0, 0  
    with torch.no_grad():  
        for X, y in test_loader:  
            X, y = X.to(device), y.to(device)  
            pred = model(X)  
  
            test_loss += loss_model(pred, y).item()  
  
    test_loss /= num_batches  
  
    print(f"Avg loss: {test_loss:>8f} \n")  
    return correct, test_loss  
  
  
def train(model, train_loader, loss_model, optimizer):  
    model = model.to(device)  
    model.train()  
  
    for i, (images, labels) in enumerate(train_loader, 0):  # 0是标起始位置的值。  
  
        images = Variable(images.to(device))  
        labels = Variable(labels.to(device))  
  
        optimizer.zero_grad()  
        outputs = model(images)  
  
        loss = loss_model(outputs, labels)  
        loss.backward()  
        optimizer.step()  
  
        if i % 1000 == 0:  
            print('[%5d] loss: %.3f' % (i, loss))  
  
test_acc_list  = []  
test_loss_list = []  
epochs = 30  
  
for t in range(epochs):  
    print(f"Epoch {t+1}\n-------------------------------")  
    train(model,train_loader,loss_model,optimizer)  
    test_acc,test_loss = test(model, test_loader, loss_model)  
    test_acc_list.append(test_acc)  
    test_loss_list.append(test_loss)  
print("Done!")  
  
  
import numpy as np  
import matplotlib.pyplot as plt  
  
from datetime import datetime  
current_time = datetime.now() # 获取当前时间  
  
x = [i for i in range(1,31)]  
  
plt.plot(x, test_loss_list, label="Loss", alpha=0.8)  
  
plt.xlabel("Epoch")  
plt.ylabel("Loss")  
plt.title(current_time) # 打卡请带上时间戳,否则代码截图无效  
  
plt.legend()  
plt.show()

image.png

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hyper-V初探

听说window自带虚拟机,小窃喜了一下,这样就不用下载第三方虚拟机软件了:VMware或者Oracle VirtualBox,但是本地搜索一看,发现没有安装,百度了一下说家庭中文版是个阉割版的系统,只有教育版&…

为AI聊天工具添加一个知识系统 之117 详细设计之58 思维导图及观察者效应 之2 概念全景图

(说明:本文和上一篇问题基本相同,但换了一个模型 deepseek-r1) Q1227、在提出项目“为使用AI聊天工具的聊天者加挂一个专属的知识系统”后,我们已经进行了了大量的讨论-持续了近三个月了。这些讨论整体淋漓尽致体现了…

数字化电子(不动产经营租赁服务)发票版式文件说明

需要具有不动产经营租赁服务开票资质,并且在资质有效期内开具发票后, 使用以下规则生成版式文件。版式文件规范中涉及到字体字号仅限于票面展示信息,不涉及填充信息。填充信息字体字号建议:购销方纳税人识别号:Courier New 12pt ; 人民币符号…

《深度学习实战》第1集:深度学习基础回顾与框架选择

本专栏系列博文旨在帮助读者从深度学习的基础知识逐步进阶到前沿技术,涵盖理论、实战和行业应用。每集聚焦一个核心知识点,并结合实际项目进行实践,避免空谈理论,简洁明快,快速切入代码,所有代码都经过验证…

科普mfc100.dll丢失怎么办?有没有简单的方法修复mfc100.dll文件

当电脑频繁弹窗提示“mfc100.dll丢失”或应用程序突然闪退时,这个看似普通的系统文件已成为影响用户体验的核心痛点。作为微软基础类库(MFC)的核心组件,mfc100.dll直接关联着Visual Studio 2010开发的大量软件运行命脉。从工业设计…

qt-C++笔记之创建和初始化 `QGraphicsScene` 和 `QGraphicsView` 并关联视图和场景的方法

qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 code review! 参考笔记 1.qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 2.qt-C笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过…

UE 播放视频

一.UI播放视频 1.导入视频文件至工程文件夹 2.文件夹内右健选择Media -> File Meida Source创建testFileMeidaSource文件。 编辑FilePath为当前视频 3.右键->Media->Media Player 创建testMediaPlayer文件 4.右键创建testMediaTexture。编辑MediaPlayer设置testMedia…

百度百舸 DeepSeek 一体机发布,支持昆仑芯 P800 单机 8 卡满血版开箱即用

在私有云环境中成功部署 DeepSeek 满血版并实现性能调优,并不是一件容易的事情。选择合适的 GPU 配置、安装相应的环境、成功部署上线业务、加速推理任务加速、支撑多用户并发 …… 完成业务测试,成功融入生产业务中。 为了帮助企业快速实现 DeepSeek 服…

改进收敛因子和比例权重的灰狼优化算法【期刊论文完美复现】(Matlab代码实现)

2 灰狼优化算法 2.1 基本灰狼优化算法 灰狼优化算法是一种模拟灰狼捕猎自然群体行为的社会启发式优化算法,属于一种新型的群体智能优化算法。灰狼优化算法具有高度的灵活性,是当前较为流行的优化算法之一。灰狼优化算法主要分为三个阶段:追…

记录一次部署PC端网址全过程

当我查看我之前写的文章时、顿时惊奇发出感慨:啥时候写的?是我写的么?疑惑重重… 所以说,好记性不如烂笔头。 记录一次部署PC端网址全过程 部署PC端网址分是三步:第一步:申请域名并映射到外网IP &#xff0…

Ollama+Deepseek+open-webui搭建本地知识库

一,Ollama下载安装 Ollama 现在完毕安装后。在命令提示符里面输入ollama -v 如看到以上提示则证明安装成功。 二,下载安装模型deepseek-r1 还是进入Ollama官网 进入模型 Ollama 选择Deepseek-r1 根据自己的电脑配置选择模型大小 进入命令提示符 …

容器化部署tomcat

容器化部署tomcat 需求在docker容器中部署tomcat,并通过外部机器访问tomcat部署的项目 容器化部署要先装好docker容器(docker安装配置) 实现步骤: 拉取tomcat docker pull tomcat用于列出本地Docker主机上存储的所有镜像 docker images在root目录里面创建tomc…

20250220-代码笔记01-class CVRPEnv

文章目录 前言一、def __init__(self, **env_params):函数功能函数代码 二、use_saved_problems(self, filename, device)函数功能函数代码 三、load_problems(self, batch_size, aug_factor1)函数功能函数代码use_saved_problems 与 load_problems 之间的关系 四、…

041集——封装之:新建图层(CAD—C#二次开发入门)

如图所示&#xff1a;增加一个图层“新图层”&#xff0c;颜色为红&#xff08;1&#xff09;&#xff0c;当图层颜色定义为黄&#xff08;2&#xff09;时&#xff0c;直接覆盖之前图层颜色&#xff0c;图层名不变。 代码如下&#xff1a; /// </summary>/// <param …

极简入门,本地部署dify低代码平台构建AI Agent大模型全流程(使用教程、微案例、配置详解、架构图解析)

文章目录 一、环境搭建1.1 安装VMware-workstationCentOS7.91.2 安装宝塔1.3 安装docker及改镜像、安装dify1.4 配置模型供应商 二、dify快速上手体验2.1 知识库2.2 微案例&#xff1a;基于知识库的助手 三、dify知识库配置详解3.1 分片策略3.2 父子分段3.3 索引方法3.4 检索结…

STM32-心知天气项目

一、项目需求 使用 ESP8266 通过 HTTP 获取天气数据&#xff08;心知天气&#xff09;&#xff0c;并显示在 OLED 屏幕上。 按键 1 &#xff1a;循环切换今天 / 明天 / 后天天气数据&#xff1b; 按键 2 &#xff1a;更新天气。 二、项目框图 三、cjson作用 https://gi…

ROS2 应用:按键控制 MoveIt2 中 Panda 机械臂关节位置

视频讲解 ROS2 应用&#xff1a;按键控制 MoveIt2 中 Panda 机械臂关节位置 创建 ROS 2 包 进入工作空间的 src 目录&#xff0c;然后创建一个新的 Python 包&#xff1a; ros2 pkg create --build-type ament_python panda_joint_control --dependencies rclpy control_msgs…

初学者如何设置以及使用富文本编辑器[eclipse版]

手把手教你设置富文本编辑器 参考来源&#xff1a;UEditor Docs 初学者按我的步骤来就可以啦 一、设置ueditor编辑器 1.提取文件[文章最底部有链接提取方式] 2.解压文件并放到自己项目中&#xff0c;在WebContent目录下&#xff1a; 3. 修改jar包位置路径 到--> 注意&a…

springboot系列十四: 注入Servlet, Filter, Listener + 内置Tomcat配置和切换 + 数据库操作

文章目录 注入Servlet, Filter, Listener官方文档基本介绍使用注解方式注入使用RegistrationBean方法注入DispatcherServlet详解 内置Tomcat配置和切换基本介绍内置Tomcat配置通过application.yml完成配置通过类配置 切换Undertow 数据库操作 JdbcHikariDataSource需求分析应用…

【数据结构初阶第十五节】堆的应用(堆排序 + Top-K问题)

必须有为成功付出代价的决心&#xff0c;然后想办法付出这个代价。云边有个稻草人-CSDN博客 对于本节我们要提前掌握前一节课堆的相关实现才能学好本次的知识&#xff0c;一定要多画图多敲代码看看实现的效果是啥&#xff08;Crazy&#xff01;&#xff09;开始吧&#xff01; …