Classifying traffic signs with Keras


I. Overview

This article mainly records the realization of traffic sign classification in the process of using Keras, and the data set is used.
The main environment used for the text is:
Python3.5.2
Tensorflow 1.7
Keras 2.1.4
win10
All programs can pass the test. In this paper, Keras will be used to classify images.
The processing process includes 1. Image preprocessing
2. Neural network training to obtain a trained model
3. Using the trained model to predict images.

2. Image preprocessing

The traffic sign images obtained in this article are obtained from a German traffic sign dataset site . Because the images obtained from the website are in PPM format, Opencv is used to convert the images from PPM to png. The specific implementation code is as follows:

import cv2
import os
# 训练集路径
ORIGINAL_TRAIN_PATH = 'datasets/Train'
# 测试集路径
ORIGINAL_TEST_PATH = 'datasets/Test'
# 处理训练集的图像,将其转换为同名称的PNG格式
for train_class in os.listdir(ORIGINAL_TRAIN_PATH):
    # train_class:当前文件夹的文件夹名称
    for pic in os.listdir(ORIGINAL_TRAIN_PATH + '/' + train_class):
        # pic:当前的PPM文件名称
        if not (pic.split('.')[1] == 'ppm'):
            continue
        # 读取图像文件
        im = cv2.imread(ORIGINAL_TRAIN_PATH + '/' + train_class + '/' + pic)
        # 获取文件名称
        name = pic.split('.')[0]
        # 生成新的文件名称
        new_name = name + '.png'
        print(new_name)
        # 生成图像文件
        cv2.imwrite('datasets/GTSRB_Final_Training_Images/GTSRB/Final_Training/Images/' + train_class + '/' + new_name, im)
# 注释与训练集解析相同
for test_class in os.listdir(ORIGINAL_TEST_PATH):
    for pic in os.listdir(ORIGINAL_TRAIN_PATH + '/' + test_class):
        if not (pic.split('.')[1] == 'ppm'):
            continue
        im = cv2.imread(ORIGINAL_TRAIN_PATH + '/' + test_class + '/' + pic)
        name = pic.split('.')[0]
        new_name = name + '.png'
        print(new_name)
        cv2.imwrite('datasets/GTSRB_Online-Test-Images-Sorted/GTSRB/Online-Test-sort/' + test_class + '/' + new_name, im)

3. Training the neural network

This process uses Keras to build a neural network. The CNN used is the classic LeNet. The experiment is relatively simple and has good applicability. For image processing, the requirements for image classification and storage:

  • Images are stored in one file type and one folder
  • Folders are represented as integers, starting from 0

The complete implementation code is as follows:

# 导入必要的模块
from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dense
from keras import backend as K
import matplotlib
matplotlib.use("Agg")
from keras.preprocessing.image import ImageDataGenerator
from keras.optimizers import Adam
from keras.preprocessing.image import img_to_array
from keras.utils import to_categorical
from imutils import paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import random
import cv2
import os
import sys
sys.path.append('..')

# matplotlib中,显示中文,置换字体
from pylab import*
mpl.rcParams['font.sans-serif'] = ['SimHei']

# 搭建的神经网络模型(LeNet)
class LeNet:
    @staticmethod
    def build(width, height, depth, classes):
        # 初始化模型
        model = Sequential()
        inputShape = (height, width, depth)
        # 如果使用了 "channels last", 更新输入shape
        if K.image_data_format() == "channels_first":  # for tensorflow
            inputShape = (depth, height, width)
        # 设置第一层 CONV => RELU => POOL 层
        model.add(Conv2D(20, (5, 5), padding="same", input_shape=inputShape))
        model.add(Activation("relu"))
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        # 设置第二层CONV => RELU => POOL 层
        model.add(Conv2D(50, (5, 5), padding="same"))
        model.add(Activation("relu"))
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        # 首先 (也是唯一设置)  FC => RELU 层
        model.add(Flatten())
        model.add(Dense(500))
        model.add(Activation("relu"))

        # softmax 分类器
        model.add(Dense(classes))
        model.add(Activation("softmax"))

        # 返回构建好的网络体系结构
        return model
# 基本的参数配置信息:
#训练迭代次数
EPOCHS = 8
#
INIT_LR = 1e-3
# 一个训练batch中的训练数据个数
BS = 64
# 分类数(分类个数,此模型是按照文件夹的个数分类的)
CLASS_NUM = 43
# 图像尺寸的大小(这个需要根据实际情况进行调整,此模型均归一化成正方形)
norm_size = 64

# 加载数据信息,图像与标签信息(图像与数字标签)
def load_data(path):
    print("[INFO] loading images...")
    # 图像数据数组即:x
    data = []
    # 标签数据数组即:y
    labels = []
    # 获取图像路径 
    imagePaths = sorted(list(paths.list_images(path)))
    random.seed(43)
    # 对图像路径随机分配处理
    random.shuffle(imagePaths)
    # 循环输入图像
    for imagePath in imagePaths:
        # 加载图像,预处理图像,并将其存储在数据列表中
        image = cv2.imread(imagePath)
        image = cv2.resize(image, (norm_size, norm_size))
        image = img_to_array(image)
        data.append(image)

        # 从图像路径中提取类标签并更新标签列表
        label = int(imagePath.split(os.path.sep)[-2])
        labels.append(label)

    # 数据进行归一化处理 将原始像素强度缩放到范围[0,1]
    data = np.array(data, dtype="float") / 255.0
    labels = np.array(labels)

    # 将标签从整数转换为矢量(即每个位置转换为0或1,)
    # to_categorical(y, num_classes=None)
    # 将类别向量(从0到nb_classes的整数向量)映射为二值类别矩阵, 
    # 用于应用到以categorical_crossentropy为目标函数的模型中.
    # y: 类别向量
    # num_classes:总共类别数
    labels = to_categorical(labels, num_classes=CLASS_NUM)
    return data, labels

# 训练神经网络
def train(aug, trainX, trainY, testX, testY, args):
    
    print("[INFO] compiling model...")
    # 初始化模型
    model = LeNet.build(width=norm_size, height=norm_size, depth=3, classes=CLASS_NUM)
    opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
    model.compile(loss="categorical_crossentropy", optimizer=opt,
                  metrics=["accuracy"])

    # 训练神经网络
    print("[INFO] training network...")
    H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS),
                            validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,
                            epochs=EPOCHS, verbose=1)

    # 将模型保存至硬盘
    print("[INFO] serializing network...")
    model.save(args["model"])

    # 绘制训练损失和准确性曲线并保存
    plt.style.use("ggplot")
    plt.figure()
    N = EPOCHS
    plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
    plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
    plt.plot(np.arange(0, N), H.history["acc"], label="train_acc")
    plt.plot(np.arange(0, N), H.history["val_acc"], label="val_acc")
    # 标题 X轴名称  Y轴名称
    plt.title("图像分类识别")
    plt.xlabel("迭代步数#")
    plt.ylabel("误差")
    plt.legend(loc="lower left")
    # 保存图像曲线
    plt.savefig(args["plot"])

# 主程序入口
if __name__=='__main__':
    args = {}
    # 存储模型的地址
    args['model'] = 'MODE/traffic_sign.model'
    # 输出训练曲线的地址
    args['plot'] = 'MODE/plot.png'
    # 训练图像集合文件夹路径
    args['dataset_train'] = "datasets/GTSRB_Final_Training_Images/GTSRB/Final_Training/Images"
    # 测试图像集合文件夹路径
    args['dataset_test'] = "datasets/GTSRB_Online-Test-Images-Sorted/GTSRB/Online-Test-sort"

    train_file_path = args['dataset_train']
    test_file_path = args['dataset_test']
    # 加载训练集合的输入端数据与输出端数据
    trainX,trainY = load_data(train_file_path)
    # 加载测试集合的输入端数据与输出端数据
    testX,testY = load_data(test_file_path)
    # 构建用于数据增强的图像生成器
    aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
        height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
        horizontal_flip=True, fill_mode="nearest")
    # 开始训练
    train(aug,trainX,trainY,testX,testY,args)

4. Image prediction

When predicting an image, the program performs the following steps:

  1. Load the trained model
  2. Perform necessary preprocessing on the input image, such as size modification and serialization;
  3. Feed the serialized image into the model
  4. Get the sequence result, find the maximum probability and the corresponding position

The testing process is to input the image, and get which classification is the most likely and the corresponding probability.
The specific implementation code is as follows:


# 加载工程中必要的库
from keras.preprocessing.image import img_to_array
from keras.models import load_model
import numpy as np
import argparse
import imutils
import cv2

# 根据使用的模型,确定图像需要resize的尺寸
norm_size = 64

# 预测函数,
# 输入: 包含配置参数的字典
def predict(args):
    
    # 加载训练好的卷积神经网络
    print("[INFO] loading network...")
    model = load_model(args["model"])

    # 加载图像
    image = cv2.imread(args["image"])
    # 因为对图像需要进行写入标签,影响较大所以复制一个图像
    orig = image.copy()

    # 预处理图像进行分类
    # 图像的尺寸重载
    image = cv2.resize(image, (norm_size, norm_size))
    # 图像的序列的归一化处理
    image = image.astype("float") / 255.0
    # 将图像进行序列化
    image = img_to_array(image)
    # 展开数组的形状.
    # 插入一个新的轴,该轴将出现在扩展阵列形状的轴位置
    image = np.expand_dims(image, axis=0)

    # 对输入的图像进行分类
    result = model.predict(image)[0]
    # print (result.shape)
    proba = np.max(result)
    label = str(np.where(result == proba)[0])
    label = "{}: {:.2f}%".format(label, proba * 100)
    print(label)
    # 在需要加载图像的情况下
    if args['show']:
        output = imutils.resize(orig, width=400)
        # 在图像上绘制标签字符串
        cv2.putText(output, label, (10, 25), cv2.FONT_HERSHEY_SIMPLEX,
                    0.7, (0, 255, 0), 2)
        # 显示带标签的图像
        cv2.imshow("Output", output)
        cv2.waitKey(0)


# python predict.py --model traffic_sign.model -i ../2.png -s
if __name__ == '__main__':
    args = {}
    # 模型的输入路径
    args['model'] = 'MODE/traffic_sign2.model'
    # 图像的输入路径
    args['image'] = 'predict/00000_00005.png'
    args['show'] = 'true'
    # 执行预测
    predict(args)

The training images are as follows:

image after training

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324651927&siteId=291194637