基于残差神经网络的交通标志识别算法研究与应用实现

news2024/11/16 3:43:06

问题:

从图像中识别交通标志对于自动驾驶至关重要。要想实现自动驾驶,车辆必须了解并遵守所有交通规则。当前,特斯拉、谷歌、梅赛德斯-奔驰、丰田、福特、奥迪等许多大公司都在研究自动驾驶。因此,为了实现这项技术的准确性,车辆应该能够解释交通标志并做出相应的决定。

摘要

在本项目项目中,我们将构建一个深度神经网络模型,并将其命名为residual_attention_network,即在残差网络的基础上引入注意力机制,并在 GTSRB交通标志数据集上进行实验,实验结果 表明改进后的残差网络在识别准确率上有明显提高,该模型经过训练可以将图像中存在的交通标志分类为不同的类别。有了这个模型,我们能够设计实现一个GUI识别上传的交通标志,这对自动驾驶汽车来说是一项非常有意义的任务。

什么是交通标志识别?

        目前道路中有几种不同类型的交通标志,如限速、禁止进入、交通信号、左转或右转、儿童过马路、重型车辆禁止通过等。交通标志分类是识别交通标志属于哪一类的重要过程和解决方式。

        交通标志自动识别是高级驾驶员辅助系统( Advanced Driver Assistance System,ADAS) 和自动驾驶领域的一个重要的研究方向。由于近年来驾驶者对汽车的智能化要求不断提高,交通标志自动识别功能逐渐被各个汽车厂商所重视。因此,越来越多相关领域的研究人员开始致力于交通标志自动识别的研究。在驾驶过程中,驾驶者可能会因为注意力不集中等原因忽视部分交通标志的提示信息,若 ADAS能及时地识别出相关的交通标志并给予驾驶者相关提示,则能够大大提升行车安全性。而在将驾驶任务完全交给行车电脑的自动驾驶领域,准确地识别道路交通标志更是一项对乘客和道路交通安全有着重大影响的任务,因此对交通标志的识别准确率要求极高。

数据集介绍

这次实验所使用的数据集是GTSRB,包括了各种气候条件下,各种各样的交通标志图像。GTSRB 数据集由 43 类交通标志组成,共有 39209 张 训练样本和 12630 张测试样本,样本像素的尺寸范围 大多为 15 × 15 到 250 × 250 之间。部分样本图像如图 4所示

该项目需要 Keras、Matplotlib、Scikit-learn、Pandas、PIL 和图像分类的代码包。

 模型设计与实现

读入数据:

我们的“train”文件夹包含 43 个文件夹,每个文件夹代表一个不同的类别。文件夹的范围是从 0 到 42。在 OS 模块的帮助下,我们遍历所有类并将图像及其各自的标签附加到数据和标签列表中。

#Retrieving the images and their labels 
for i in range(classes):
    path = os.path.join(cur_path,'train',str(i))
    images = os.listdir(path)
    for a in images:
        try:
            image = Image.open(path + '\\'+ a)
            image = image.resize((30,30))
            image = np.array(image)
            #sim = Image.fromarray(image)
            data.append(image)
            labels.append(i)
        except:
            print("Error loading image")
#Converting lists into numpy arrays
data = np.array(data)
labels = np.array(labels)

上述过程,我们将所有图像及其标签存储到列表(数据和标签)中。但是,我们需要将列表转换为 numpy 数组以提供给模型。数据的形状是 (39209, 30, 30, 3) 这意味着有 39209 张大小为 30×30 像素的图像,最后 3 意味着数据包含彩色图像(RGB 值)。

在 sklearn 包中,我们使用 train_test_split() 方法来拆分训练和测试数据。并使用 to_categorical 方法将 y_train 和 t_test 中存在的标签转换为单热编码。

X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=0.2, random_state=42)
#Converting the labels into one hot encoding
y_train = to_categorical(y_train, 43)
y_test = to_categorical(y_test, 43)

 基本CNN 模型构建

def cnn_model():
    model = Sequential()

    model.add(Conv2D(32, (3, 3), padding='same',
                     input_shape=(3, IMG_SIZE, IMG_SIZE),
                     activation='relu'))
    model.add(Conv2D(32, (3, 3), activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.2))

    model.add(Conv2D(64, (3, 3), padding='same',
                     activation='relu'))
    model.add(Conv2D(64, (3, 3), activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.2))

    model.add(Conv2D(128, (3, 3), padding='same',
                     activation='relu'))
    model.add(Conv2D(128, (3, 3), activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.2))

    model.add(Flatten())
    model.add(Dense(512, activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(NUM_CLASSES, activation='softmax'))
    return model
  • loss:我们要优化的损失函数。选择categorical_crossentropy
  • optimizer:我们使用带有 Nesterov 动量的标准随机梯度下降。
  • metric:由于我们正在处理分类问题,因此我们的指标是准确性。

构建residual_attention_network模型

模型结构如下:

该部分完整代码和相关数据集可以在此处下载:

https://download.csdn.net/download/weixin_40651515/87358141

from keras.layers import Input, Conv2D, Lambda, MaxPool2D, UpSampling2D, AveragePooling2D, ZeroPadding2D
from keras.layers import Activation, Flatten, Dense, Add, Multiply, BatchNormalization, Dropout
from keras.models import Model
# Todo: Make scalable/all-encompassing
class ResidualAttentionNetwork():

    def __init__(self, input_shape, n_classes, activation, p=1, t=2, r=1): self.input_shape = input_shape
        self.n_classes = n_classes
        self.activation = activation
        self.p = p
        self.t = t
        self.r = r
        
    def build_model(self):
         # Initialize a Keras Tensor of input_shape
        input_data = Input(shape=self.input_shape)
        
        # Initial Layers before Attention Module
        
        # Doing padding because I'm having trouble with img dims that are <= 28
        if self.input_shape[0] <= 28 or self.input_shape[1] <= 28:
            x_dim_inc = (32 - self.input_shape[0]) // 2
            y_dim_inc = (32 - self.input_shape[1]) // 2
            
            # Pad the input data to 32x32
            padded_input_data = ZeroPadding2D( (x_dim_inc, y_dim_inc) )(input_data)
            conv_layer_1 = Conv2D(filters=32,
                             kernel_size=(3,3),
                             strides=(1,1),
                             padding='same')(padded_input_data)
        else:
            conv_layer_1 = Conv2D(filters=32,
                             kernel_size=(3,3),
                             strides=(1,1),
                             padding='same')(input_data)
        
        max_pool_layer_1 = MaxPool2D(pool_size=(2, 2), 
                                     strides=(2, 2),
                                     padding='same')(conv_layer_1)

        # Residual Unit then Attention Module #1
        res_unit_1 = self.residual_unit(max_pool_layer_1, filters=[32, 64, 128])
        att_mod_1 = self.attention_module(res_unit_1, filters=[32, 64, 128])
        
        # Residual Unit then Attention Module #2
        res_unit_2 = self.residual_unit(att_mod_1, filters=[32, 64, 128])
        att_mod_2 = self.attention_module(res_unit_2, filters=[32, 64, 128])

        # Residual Unit then Attention Module #3
        res_unit_3 = self.residual_unit(att_mod_2, filters=[32, 64, 128])
        att_mod_3 = self.attention_module(res_unit_3, filters=[32, 64, 128])
        res_unit_end_1 = self.residual_unit(att_mod_3, filters=[32, 32, 64])
        res_unit_end_2 = self.residual_unit(res_unit_end_1, filters=[32, 32, 64])
        res_unit_end_3 = self.residual_unit(res_unit_end_2, filters=[32, 32, 64])
        res_unit_end_4 = self.residual_unit(res_unit_end_3, filters=[32, 32, 64])
        avg_pool_layer = AveragePooling2D(pool_size=(2, 2), strides=(2, 2))(res_unit_end_4)

        # Flatten the data
        flatten_op = Flatten()(avg_pool_layer)
        # FC Layers for prediction
        fully_connected_layer_1 = Dense(256, activation='relu')(flatten_op)
        dropout_layer_1 = Dropout(0.5)(fully_connected_layer_1)
        fully_connected_layer_2 = Dense(256, activation='relu')(dropout_layer_1)
        dropout_layer_2 = Dropout(0.5)(fully_connected_layer_2)
        fully_connected_layer_3 = Dense(256, activation='relu')(dropout_layer_2)
        dropout_layer_3 = Dropout(0.5)(fully_connected_layer_3)
        fully_connected_layer_last = Dense(self.n_classes, activation=self.activation)(dropout_layer_3)
         
        # Fully constructed model
        model = Model(inputs=input_data, outputs=fully_connected_layer_last)
        
        return model

    # Pre-Activation Identity ResUnit Bottleneck Architecture
    def residual_unit(self, residual_input_data, filters): 
        # Hold input_x here for later processing
        identity_x = residual_input_data
        filter1,filter2,filter3 = filters 
        # Layer 1
        batch_norm_op_1 = BatchNormalization()(residual_input_data)
        activation_op_1 = Activation('relu')(batch_norm_op_1)
        conv_op_1 = Conv2D(filters=filter1,
                         kernel_size=(1,1),
                         strides=(1,1),
                         padding='same')(activation_op_1) 
        # Layer 2
        batch_norm_op_2 = BatchNormalization()(conv_op_1)
        activation_op_2 = Activation('relu')(batch_norm_op_2)
        conv_op_2 = Conv2D(filters=filter2,
                         kernel_size=(3,3),
                         strides=(1,1),
                         padding='same')(activation_op_2)
        # Layer 3
        batch_norm_op_3 = BatchNormalization()(conv_op_2)
        activation_op_3 = Activation('relu')(batch_norm_op_3)
        conv_op_3 = Conv2D(filters=filter3,
                         kernel_size=(1,1),
                         strides=(1,1),
                         padding='same')(activation_op_3)
        # Element-wise Addition
        if identity_x.shape[-1].value != conv_op_3.shape[-1].value:
            filter_n = conv_op_3.shape[-1].value
            
            identity_x = Conv2D(filters=filter_n,
                             kernel_size=(1,1),
                             strides=(1,1),
                             padding='same')(identity_x)  
        output = Add()([identity_x, conv_op_3])
        return output

    def attention_module(self, attention_input_data, filters):
        # Send input_x through #p residual_units
        p_res_unit_op_1 = attention_input_data
        for _ in range(self.p):
            p_res_unit_op_1 = self.residual_unit(p_res_unit_op_1, filters=filters)
        # Perform Trunk Branch Operation
        trunk_branch_op = self.trunk_branch(trunk_input_data=p_res_unit_op_1, filters=filters)
        # Perform Mask Branch Operation
        mask_branch_op = self.mask_branch(mask_input_data=p_res_unit_op_1, filters=filters)
        # Perform Attention Residual Learning: Combine Trunk and Mask branch results
        ar_learning_op = self.attention_residual_learning(mask_input=mask_branch_op, trunk_input=trunk_branch_op)
        # Send branch results through #p residual_units
        p_res_unit_op_2 = ar_learning_op
        for _ in range(self.p):
            p_res_unit_op_2 = self.residual_unit(p_res_unit_op_2, filters=filters)
        return p_res_unit_op_2
    def trunk_branch(self, trunk_input_data, filters):
        # sequence of residual units, default=2
        t_res_unit_op = trunk_input_data
        for _ in range(self.t):
            t_res_unit_op = self.residual_unit(t_res_unit_op, filters=filters)
        return t_res_unit_op

    def mask_branch(self, mask_input_data, filters, m=3):
        downsampling = MaxPool2D(pool_size=(2, 2), 
                                     strides=(2, 2),
                                     padding='same')(mask_input_data)

        for _ in range(m):
            # Perform residual units ops r times between adjacent pooling layers
            for j in range(self.r):
                downsampling = self.residual_unit(residual_input_data=downsampling, filters=filters)

            # Last pooling step before middle step - Bottom
            downsampling = MaxPool2D(pool_size=(2, 2), 
                                         strides=(2, 2),
                                         padding='same')(downsampling)
        
        # Upsampling Step Initialization - Top
        upsampling = UpSampling2D(size=(2, 2))(middleware)

        for _ in range(m):
            # Perform residual units ops r times between adjacent pooling layers
            for j in range(self.r):
                upsampling = self.residual_unit(residual_input_data=upsampling, filters=filters)

            # Last interpolation step - Bottom
            upsampling = UpSampling2D(size=(2, 2))(upsampling)
        
        conv_filter = upsampling.shape[-1].value
        
        conv1 = Conv2D(filters=conv_filter, kernel_size=(1,1),strides=(1,1), padding='same')(upsampling)
        
        conv2 = Conv2D(filters=conv_filter,kernel_size=(1,1),strides=(1,1),padding='same')(conv1)

        sigmoid = Activation('sigmoid')(conv2)

        return sigmoid

    def attention_residual_learning(self, mask_input, trunk_input):
        # https://stackoverflow.com/a/53361303/9221241
        Mx = Lambda(lambda x: 1 + x)(mask_input) # 1 + mask
        return Multiply()([Mx, trunk_input]) # M(x) * T(x)

调用:

model = ResidualAttentionNetwork((32, 32, 3),43, activation='softmax').build_model()
model.compile(optimizer=optimizers.Adam(lr=0.0001),loss='categorical_crossentropy',metrics=['accuracy'])
history = model.fit(X_train, y_train, batch_size=32, epochs=epochs, validation_data=(X_test, y_test))

构建模型架构后,我们使用 model.fit() 训练模型。我尝试了批大小 32 和 64。我们的模型在批大小为 64 时表现更好。并且在 15 个 epoch 之后精度稳定。实验结果:

基本CNN 模型构建

交通标志分类器 GUI

        现在我们将使用 Tkinter 为我们的交通标志分类器构建一个图形用户界面。Tkinter 是标准 python 库中的一个 GUI 工具包。在项目文件夹中创建一个新文件并复制以下代码。将其保存为 gui.py,您可以通过在命令行中键入 python gui.py 来运行代码。在此文件中,我们首先使用 Keras 加载了经过训练的模型“traffic_classifier.h5”。然后我们构建用于上传图像的 GUI,并使用一个按钮进行分类,调用 classify() 函数。classify() 函数将图像转换为形状维度 (1, 30, 30, 3)。这是因为要预测交通标志,我们必须提供与构建模型时相同的维度。然后我们预测类model.predict_classes(image) 返回一个介于 (0-42) 之间的数字,代表它所属的类别。我们使用字典来获取有关类的信息。这是 gui.py 文件的代码。

结论

        本文在 ResNet 网络基础上引入注意力机制。首先通过注意力机制来使 模型在特征提取过程中,更加关注图像中包含重要特征的区域。实验表明,改进后的 ResNet 模型识别准 确率有明显提高,能够更好地满足交通标志识别任务 的高准确率要求。所提出的方法虽然有效地提升了交通标志自动识别的准确率,但是依然存在一些问 题,如增加了模型复杂度,计算量有一定增加,实时性方面有一定局限性。因此,如何在保证识别准确率的 前提下,降低模型复杂度,减少算法所需时间和计算 资源,更好地满足实时性应用和需求,将是下一步的研究方向。

var code = "483094a5-d455-402d-9eb5-73d1381f55ae"

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

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

相关文章

pandas的series创建和pandans的dataFrame创建

一&#xff1a;series和读取外部数据 1.1pandas的series的了解 1.1.1 为什么要学习pandas numpy能够帮我们处理处理数值型数据&#xff0c;但是这还不够。很多时候&#xff0c;我们的数据除了数值之外&#xff0c;还有字符串&#xff0c;还有时间序列等 比如&#xff1a;我们通…

显式利用用户画像的多兴趣建模

显式利用用户画像的多兴趣建模 目前在多兴趣建模中&#xff0c;用户侧的特征包括用户基础画像特征&#xff08;年龄、性别、地域等&#xff09;、用户在当前场景的静态兴趣画像特征&#xff08;短期兴趣画像、长期兴趣画像&#xff09;、交互的历史正向行为序列特征&#xff0…

【Javassist】快速入门系列13 使用Javassist获取注解

系列文章目录 01 在方法体的开头或结尾插入代码 02 使用Javassist实现方法执行时间统计 03 使用Javassist实现方法异常处理 04 使用Javassist更改整个方法体 05 当有指定方法调用时替换方法调用的内容 06 当有构造方法调用时替换方法调用的内容 07 当检测到字段被访问时使用语…

MySQL性能优化三 一条SQL在MySQL中执行的过程

一 MySQL的内部组件结构 大体来说&#xff0c;MySQL 可以分为 Server 层和存储引擎层两部分。 1.1 service层 主要包括连接器、查询缓存、分析器、优化器、执行器等&#xff0c;涵盖 MySQL 的大多数核心服务功能&#xff0c;以及所有的内置函数&#xff08;如日期、时间、数学…

Easy-Captcha验证码 生成以及校验(简单易懂)

目录说明pom引入详解参数类使用easy-captcha 中提供了下面几种类源码说明Captcha使用验证图解源码测试GitHub说明 Java图形验证码&#xff0c;支持gif、中文、算术等类型&#xff0c;可用于Java Web、JavaSE等项目 pom引入 <dependency><groupId>com.github.whvc…

【C++】 bitset(位图)的使用

目录 一、bitset的基本介绍 1. 位图的概念 2. 位图的应用 二、biset的基本使用 1. bitset的成员函数 2. 基本使用介绍 1. 定义方式 2. 成员函数的使用 一、bitset的基本介绍 1. 位图的概念 所谓位图&#xff0c;就是用每一位来存放某种状态&#xff0c;适用于海量数…

win系统一台电脑安装两个不同版本的mysql教程

1.mysql下载zip包&#xff08;地址&#xff09;MySQL :: Download MySQL Community Serverhttps://dev.mysql.com/downloads/mysql/ 2.解压在你的电脑上&#xff08;不要再C盘和带中文的路径&#xff09; data和my.ini是没有的。 3.创建my.ini文件 创建记事本改变后缀名就可以 …

【5G RRC】小区搜索(Cell Search)和系统捕获(System Acquisition)流程

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

环境变量?拿来把你!

文章目录环境变量直接运行程序的第一种方法&#xff1a;把程序移动到系统目录底下echo $环境变量&#xff1a;查看环境变量PATH:指定命令的搜索路径export 定义一个新的环境变量export PATH旧路径&#xff1a;新路径getenv&#xff1a;获取环境变量—获取环境变量的第一种方式s…

车载诊断协议UDS——读写服务Service 22/2E

在UDS协议中,对于服务常用有两种格式: 1、Service (服务) + Subfunction( 子服务) 子服务可理解为对服务的功能补充,比如会话模式Service 10服务,子服务可以分为不同的会话模式(默认会话模式、扩展会话模式、编程会话模式等等),用来区分服务的执行权限。 2、Servi…

[OC学习笔记]启动流程(objc部分)

先回顾下这张图&#xff0c;回顾下整体流程。现在分析下在此流程中objc4源码&#xff08;818.2&#xff09;的处理逻辑。 _objc_init解析 我们在上图可以看出&#xff0c;dyld在main函数之前&#xff08;pre-main&#xff09;会间接调用到objc的_objc_init&#xff0c;其中使…

洛谷—— AT_abc157_a [ABC157A] Duplex Printing

文章目录[ABC157A] Duplex Printing题面翻译题目描述输入格式输出格式说明提示题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1样例 #2样例输入 #2样例输出 #2样例 #3样例输入 #3样例输出 #3提示制約Sample Explanation 1AC代码[ABC157A] Duplex Printing 题面翻译 题…

GC调优

GC调优一、新生代调优二、幸存区调优三、老年代调优四、GC调优案例案例一&#xff1a;Full GC和Minor GC频繁案例二&#xff1a;请求高峰期发生Full GC&#xff0c;单次暂停时间特别长&#xff08;CMS&#xff09;案例三&#xff1a;老年代充裕情况下&#xff0c;发生Full GC&a…

SQL中灵活的视图

文章目录视图的创建、嵌套及特性创建视图查询视图视图的嵌套常见的8个使用场景场景一&#xff1a;仅提供需要的数据场景二&#xff1a;对特定的用户仅开放特定的数据&#xff0c;达到保护敏感数据的目的&#xff0c;提升了数据安全性&#xff1b;仅筛选需要的数据场景四&#x…

迭代器模式 实现ES大量数据查询

目录 项目需求 要求 普通策略 升级策略&#xff1a;使用迭代器模式 迭代器模式组成 代码实现 查询实体 返回实体 实现类 代码测试 mock的ES返回结果json数据 第一次返回结果 第二次返回结果 第三次返回结果 postMan请求, 控制台打印结果 项目需求 数据从Mysq…

云计算服务安全指南

声明 本文是学习GB-T 31167-2014 信息安全技术 云计算服务安全指南. 下载地址而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 云计算服务安全退出服务 9.1退出要求 合同到期或其他原因都可能导致客户退出云计算服务&#xff0c;或将数据和业务系统迁…

植物大战僵尸:代码实现自动收集阳光

通过阳光增加的值为切入点&#xff0c;找到自动收集阳光的关键判断并实现自动收集阳光&#xff0c;首先我们猜测当阳光出现后&#xff0c;我们是否会去点击&#xff0c;这个过程必然是由一个判断和一个时钟周期事件来控制的&#xff0c;那么当我们点击下落的阳光以后&#xff0…

DC-UNet:重新思考UNet架构和双通道高效CNN医学图像

摘要 经典UNet的体系架构在某些方面存在着局限性。因此本文对其结构提出了改进。1)设计高效的CNN架构来取代编码器和解码器;2)在最先进的U-Net模型的基础上&#xff0c;应用残差模块来取代编码器和解码器之间的跳过连接来进行改进。 医学图像分割是通过一些自动和半自动的方法…

linux系统中块设备的基本实现方法

大家好&#xff0c;今天主要和大家聊一聊&#xff0c;如何使用linux系统中的块设备的实现方法。 目录 第一&#xff1a;块设备基本简介 第二&#xff1a;块设备驱动框架 第三&#xff1a;实现程序代码实现 第一&#xff1a;块设备基本简介 块设备驱动与字符设备驱动之间的主…

双指针:环形链表II

题目&#xff1a;142. 环形链表 II 我们知道&#xff0c;判断一个链表是否为环是这样的&#xff1a; public boolean hasCycle(ListNode head) {ListNode slow head,quickly head;while(quickly ! null && quickly.next ! null){slow slow.next;quickly quickly.n…