基于GAN的自动驾驶汽车语义分割

共 10257字,需浏览 21分钟

 ·

2021-03-22 10:19

点击上方小白学视觉”,选择加"星标"或“置顶

重磅干货,第一时间送达

语义分割是计算机视觉中的关键概念之一,语义分割允许计算机通过按类型对图像中的对象进行颜色编码。GAN建立在基于真实内容的基础上复制和生成原始内容的概念上,这使它们适合于在街景图像上进行语义分割的任务,不同部分的分割使在环境中导航的代理能够适当地发挥作用。


数据预处理


我们从一个kaggle数据集获取数据,街景和分割的图像被配对在一起。这意味着为了构建数据集,必须将每个图像分成两部分,以分割每个实例的语义图像和街景图像。

from PIL import Imagefrom IPython.display import clear_outputimport numpy as npsemantic = []real = []semantic_imgs = []real_imgs = []counter = 0for img in img_paths:    if 'jpg' in img:        im = Image.open(img)        left = 0        top = 0        right = 256        bottom = 256        real_img = im.crop((left, top, right, bottom))         real_imgs.append(real_img)        real.append(np.array(real_img.getdata()).reshape(256,256,3))        left = 256        top = 0        right = 512        bottom = 256        semantic_img = im.crop((left, top, right, bottom))         semantic_imgs.append(semantic_img)        semantic.append(np.array(semantic_img.getdata()).reshape(256,256,3))        counter += 1        print(counter)        if counter % 10 == 0:          clear_output()    else:        print(img)

该脚本将每个图像裁剪为两个,并记录像素值和原始图像。原始图像也被记录下来,因此以后无需再进行显示。

import numpy as npsemantic = np.array(semantic)real = np.array(real)X = realy = semantic

将两个列表都转换为numpy数组后,可以直接定义x和y值。实际上,根据目标,你们可以切换x和y值以控制模型的输出。在这种情况下,我们想将真实图像转换为语义图像。但是,稍后我们将尝试训练GAN将语义数据转换为真实数据。


构建GAN 

from numpy import expand_dimsfrom numpy import zerosfrom numpy import onesfrom numpy import vstackfrom numpy.random import randnfrom numpy.random import randintfrom keras.utils import plot_modelfrom keras.models import Modelfrom keras.layers import Inputfrom keras.layers import Densefrom keras.layers import Flattenfrom keras.layers.convolutional import Conv2D,Conv2DTransposefrom keras.layers.pooling import MaxPooling2Dfrom keras.layers.merge import concatenatefrom keras.initializers import RandomNormalfrom keras.layers import LeakyReLUfrom keras.layers import BatchNormalizationfrom keras.layers import Activation,Reshapefrom keras.optimizers import Adamfrom keras.models import Sequentialfrom keras.layers import Dropoutfrom IPython.display import clear_outputfrom keras.layers import Concatenate

当我们使用keras框架构造生成器和鉴别器时,我们需要导入所有必需的图层类型以构造模型。这包括主要的卷积和卷积转置层,以及批处理归一化层和泄漏的relu层。串联层用于构建U-net体系结构,因为它可以将某些层链接在一起。

def define_discriminator(image_shape=(256,256,3)):    init = RandomNormal(stddev=0.02)    in_src_image = Input(shape=image_shape)    in_target_image = Input(shape=image_shape)    merged = Concatenate()([in_src_image, in_target_image])    d = Conv2D(64, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(merged)    d = LeakyReLU(alpha=0.2)(d)    d = Conv2D(128, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d)    d = BatchNormalization()(d)    d = LeakyReLU(alpha=0.2)(d)    d = Conv2D(256, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d)    d = BatchNormalization()(d)    d = LeakyReLU(alpha=0.2)(d)    d = Conv2D(512, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d)    d = BatchNormalization()(d)    d = LeakyReLU(alpha=0.2)(d)    d = Conv2D(512, (4,4), padding='same', kernel_initializer=init)(d)    d = BatchNormalization()(d)    d = LeakyReLU(alpha=0.2)(d)    d = Conv2D(1, (4,4), padding='same', kernel_initializer=init)(d)    patch_out = Activation('sigmoid')(d)    model = Model([in_src_image, in_target_image], patch_out)    opt = Adam(lr=0.0002, beta_1=0.5)    model.compile(loss='binary_crossentropy', optimizer=opt, loss_weights=[0.5])    return model

该判别器是pix2pix GAN论文使用的模型的keras实现。使用泄漏的Relu而不是正常的Relu是为了使负值仍然被考虑在内。这增加了收敛速度。鉴别器执行二进制分类,因此在最后一层使用S形,并使用二进制交叉熵作为损失函数。

def define_encoder_block(layer_in, n_filters, batchnorm=True):    init = RandomNormal(stddev=0.02)    g = Conv2D(n_filters, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(layer_in)    if batchnorm:        g = BatchNormalization()(g, training=True)    g = LeakyReLU(alpha=0.2)(g)    return g def decoder_block(layer_in, skip_in, n_filters, dropout=True):    init = RandomNormal(stddev=0.02)    g = Conv2DTranspose(n_filters, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(layer_in)    g = BatchNormalization()(g, training=True)    if dropout:        g = Dropout(0.5)(g, training=True)    g = Concatenate()([g, skip_in])    g = Activation('relu')(g)    return g

生成器包括多次对初始数据进行编码,直到获得原始图像的特征图为止。然后将此特征图像解码,直到获得完整分辨率的图像为止。这意味着生成器中的大多数层只是编码器和解码器块。在对编码器解码器块进行了精心设计之后,为了构建生成器,没有更多的工作要做。

def define_generator(image_shape=(256,256,3)):    init = RandomNormal(stddev=0.02)    in_image = Input(shape=image_shape)    e1 = define_encoder_block(in_image, 64, batchnorm=False)    e2 = define_encoder_block(e1, 128)    e3 = define_encoder_block(e2, 256)    e4 = define_encoder_block(e3, 512)    e5 = define_encoder_block(e4, 512)    e6 = define_encoder_block(e5, 512)    e7 = define_encoder_block(e6, 512)    b = Conv2D(512, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(e7)    b = Activation('relu')(b)    d1 = decoder_block(b, e7, 512)    d2 = decoder_block(d1, e6, 512)    d3 = decoder_block(d2, e5, 512)    d4 = decoder_block(d3, e4, 512, dropout=False)    d5 = decoder_block(d4, e3, 256, dropout=False)    d6 = decoder_block(d5, e2, 128, dropout=False)    d7 = decoder_block(d6, e1, 64, dropout=False)    g = Conv2DTranspose(3, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d7)    out_image = Activation('tanh')(g)    model = Model(in_image, out_image)    return model

使用多个编码器和解码器,我们得到了这个生成器。使用双曲正切可对数据进行归一化,范围从(0,255)到(-1,1)。我们必须记住将数据编码为范围(-1,1),这样才能正确评估生成器的输出和y值。

def define_gan(g_model, d_model, image_shape):    d_model.trainable = False    in_src = Input(shape=image_shape)    gen_out = g_model(in_src)    dis_out = d_model([in_src, gen_out])    model = Model(in_src, [dis_out, gen_out])    opt = Adam(lr=0.0002, beta_1=0.5)    model.compile(loss=['binary_crossentropy', 'mse'], optimizer=opt)    return model

将两个模型连接在一起即可得到完整的GAN。发生器的输出直接馈入鉴别器。


生成样本

def generate_real_samples(dataset, n_samples, patch_shape):    trainA, trainB = dataset    ix = randint(0, trainA.shape[0], n_samples)    X1, X2 = trainA[ix], trainB[ix]    y = ones((n_samples, patch_shape, patch_shape, 1))    X1 = (X1 - 127.5) / 127.5    X2 = (X2 - 127.5) / 127.5    return [X1, X2], y def generate_fake_samples(g_model, samples, patch_shape):    X = g_model.predict(samples)    y = zeros((len(X), patch_shape, patch_shape, 1))    return X, y

为了使鉴别器起作用,必须同时提供真实样本和计算机生成的样本。但是,该过程并不是那么简单,需要对这些值进行标准化。由于像素值的范围介于0到255之间,因此通过使用等式X1 =(X1–127.5)/ 127.5,所有值都将在(-1,1)范围内进行归一化。


执行程序

def train(d_model, g_model, gan_model, dataset, n_epochs=100, n_batch=10):    n_patch = d_model.output_shape[1]    trainA, trainB = dataset    bat_per_epo = int(len(trainA) / n_batch)    n_steps = bat_per_epo * n_epochs    for i in range(n_steps):        [X_realA, X_realB], y_real = generate_real_samples(dataset, n_batch, n_patch)        X_fakeB, y_fake = generate_fake_samples(g_model, X_realA, n_patch)        d_loss1 = d_model.train_on_batch([X_realA, X_realB], y_real)        d_loss2 = d_model.train_on_batch([X_realA, X_fakeB], y_fake)        g_loss, _, _ = gan_model.train_on_batch(X_realA, [y_real, X_realB])        print('>%d, d1[%.3f] d2[%.3f] g[%.3f]' % (i+1, d_loss1, d_loss2, g_loss))        if (i+1) % 100 == 0:            clear_output()

此功能训练GAN。这里要注意的关键是批次大小。该论文建议使用迷你们批处理(n_batch = 1),但经过一些测试,我们发现批处理大小为10会产生更好的结果。

image_shape = (256,256,3)d_model = define_discriminator()g_model = define_generator()gan_model = define_gan(g_model, d_model, image_shape)train(d_model, g_model, gan_model, [X,y])

该脚本定义图像形状,并调用函数来构造GAN的不同部分。然后,它调用训练功能来训练模型。


结果


真实到语义:

尽管计算机生成的图像模糊,但可以正确对图像中的所有内容进行颜色编码。请记住,计算机无法看到真实图像的实际语义表示!我们认为图像是模糊的,因为真正的256 x 256图像不是很复杂,而且有许多可能使机器掉色的颜色。右边的图像(计算机生成)可以分割成正方形。如果计算这些平方,它将与卷积层的过滤器数量匹配!

语义到真实:

将语义数据转换为真实的街景图像时,我们担心这是不可能的,因为当转换为语义数据时,会丢失大量数据。例如,红色汽车和绿色汽车都变成蓝色,因为汽车是按蓝色像素分类的。这是一个明显的问题。可能具有不同颜色的对象根本没有出现,从而导致图像看起来只有一点点相似。看一下下面的图片:


结论 


考虑到该网络仅训练了10个纪元,我们认为该项目是成功的,并且结果似乎很有希望。我们希望人们可以玩弄模型架构和超参数,以提高GAN创建的图像的质量。


下载1:OpenCV-Contrib扩展模块中文版教程
在「小白学视觉」公众号后台回复:扩展模块中文教程即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。

下载2:Python视觉实战项目52讲
小白学视觉公众号后台回复:Python视觉实战项目即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。

下载3:OpenCV实战项目20讲
小白学视觉公众号后台回复:OpenCV实战项目20讲即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。

交流群


欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~


浏览 41
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报