释放Pix2Pix-使用创意超能力转换图像

释放Pix2Pix:利用创意超能力,实现图像转换' (Shìfàng Pix2Pix Lìyòng chuàngyì chāonénglì, shíxiàn túxiàng zhuǎnhuàn)

介绍

想象一下一种特殊的电脑程序,它能够让孩子们画的图画变得活灵活现。你知道孩子们画的那些色彩丰富、富有想象力的画吗?这个程序可以将这些画变成真实的图像,几乎像魔法一样!它被称为 Pix2Pix。我们知道魔术师是如何在用一副扑克牌上展现惊人技巧的。类似地,Pix2Pix 可以用画作创造出惊人的效果。Pix2Pix 在计算机理解和处理图片方面带来了重大变革。它让我们对所创建的图片拥有非常精确的控制。它就像拥有一个制作和修改图像的超能力!

来源:X.com

学习目标

  • 了解 Pix2Pix 是什么,它是如何工作的,并探索它的真实应用
  • 通过使用 Pix2Pix 将绘画转化为图像,使用建筑立面数据集来尝试它
  • 了解 Pix2Pix 的工作原理以及它如何解决许多图像到图像翻译任务所面临的问题

本文是作为数据科学博文大赛的一部分发表的。

生成对抗网络(GANs)

人工智能领域最令人激动的最近发明之一是生成对抗网络(GAN)。

这些强大的神经网络可以创建新内容,包括图像、音乐和文本。GAN 包含两个神经网络。一个是生成器,负责创建内容;另一个是判别器,负责评判生成的内容。

生成器负责创建内容。它从随机噪声或数据开始,并逐步将其精炼成有意义的东西。例如,在图像生成中,它可以从零开始创建图像。它可以通过调整随机像素值使其更像精美、真实的图像。判别器的作用是评估生成器生成的内容。它决定内容是真实还是虚假。随着它检查更多内容并向生成器提供反馈,随着训练的进行,判别器变得越来越好。

来源:Neptune.ai

GAN 的整个训练过程被称为对抗训练。这个过程非常简单易懂。生成器创建的内容最初与完美相距甚远。判别器评估内容,也就是试图区分真假。生成器从判别器那里得到反馈,并调整内容使其更具说服力,这样提供的内容比之前更好。作为回应生成器的改进,判别器改善了检测虚假内容的能力。这样,对抗训练不断使 GANs 变得更加强大。

Pix2Pix

图像转换和处理的概念始于传统图像处理技术。包括图像调整大小、颜色校正和滤镜等。然而,这些传统方法在更复杂的任务,如图像到图像的转换方面存在局限性。机器学习,特别是深度学习,已经彻底改变了图像转换领域。卷积神经网络(CNNs)在自动化图像处理任务方面变得越来越重要。然而,生成对抗网络(GANs)的发展标志着图像到图像转换的重大突破。

Pix2Pix 是用于图像转换任务的深度学习模型。Pix2Pix 的核心思想是将一个域的输入图像转换成另一个域中对应的输出图像。它可以将图像从一种风格转换为另一种风格。这种方法被称为条件生成对抗网络(cGAN),因为 Pix2Pix 利用了一种条件设置,其中输入图像作为生成器的条件。根据这个条件,输出图像将被生成。

来源:Phillipi

条件生成对抗网络(CGAN)是GAN框架的先进版本,可以精确控制生成的图像。它可以生成特定类别的图像。Pix2Pix GAN是CGAN的一个实例,其中生成图像的过程取决于另一张给定图像的存在。在这张图片中,我们可以看到pix2pix创造的奇迹。我可以根据标签创建街景、立面、黑白转换为彩色、航拍视图转换为真实地图、白天照片转换为夜景,并基于边缘创建照片。

图像到图像的转换挑战

图像到图像的转换是一项具有挑战性的计算机视觉任务,特别是当目标是将一个域中的图像转换为另一个域中的图像时。在这里,它必须保留底层内容和结构。图像到图像转换的挑战在于捕捉输入和输出域之间的复杂关系。解决这个问题的一个突破性解决方案之一是Pix2Pix。

生成的图像有时可能存在问题,如模糊或失真。Pix2pix通过使用两个网络来改进图像外观:一个用于生成图像的生成器和另一个用于检查它们是否真实的判别器。判别器帮助生成器生成更清晰、更接近真实照片的图像,从而减少模糊和失真问题。

在像图像上色这样的任务中,生成图像中的颜色可能会扩散到相邻区域,导致颜色分布不真实。Pix2pix使用条件GAN等技术更好地控制上色过程。这使得上色看起来更自然,减少了凌乱感。

Pix2Pix架构

Pix2Pix的架构由两个主要组件组成:生成器和判别器。构建生成器和判别器模型的常见方法包括使用由Convolution-BatchNormalization-ReLU等层组成的标准构建块。将这些构建块组合形成深度卷积神经网络。

U-Net生成器模型

在这里,生成器使用U-Net模型架构。传统的编码器-解码器模型将图像作为输入,并对其进行几层的下采样。该过程持续进行,直到图像中的一个层经过几层上采样,并输出最终图像。UNet架构还涉及再次对图像进行下采样和上采样。但这里的区别在于它在编码器和解码器中的相同大小层之间具有跳跃连接。跳跃连接使模型能够结合低级和高级特征,解决了下采样过程中信息丢失的问题。

U形状的顶部部分由一系列卷积和池化层组成,逐渐减小输入图像的空间尺寸,同时增加特征通道的数量。这部分网络负责从输入图像中捕捉上下文信息。U-Net已经成为深度学习中用于图像分割任务的基础架构。最后,这个生成器将生成与真实图像无法区分的图像。

来源:GitHub

PatchGAN鉴别器模型

设计鉴别器模型以接受两个图像作为输入。它接收来自源域和目标域的图像。其主要任务是评估并确定图像是真实的还是生成器生成的概率。

鉴别器模型使用传统GAN结合深度卷积神经网络对图像进行分类。Pix2Pix鉴别器使用PatchGAN而不是传统GAN。它不是将完整的输入图像分类为真实或伪造的,而是设计这个深度卷积神经网络来识别图像的补丁。它将真实和生成的图像分割为较小的非重叠补丁,并逐个评估每个补丁。PatchGAN为生成器提供细粒度的反馈,并使其集中于改进本地图像细节。这使得生成器的训练更好。它在一些需要保留细节的任务中非常有用。这些任务包括图像超分辨率。它有助于生成高分辨率和逼真的结果。

来源:ResearchGate

Pix2Pix的应用

现在让我们来看看Pix2Pix的一些应用。

  • 建筑设计:Pix2Pix可以将建筑设计的草图转换为详细的建筑蓝图。这有助于建筑师设计更好的建筑。
  • 风格转换:它可以将一幅图像的风格转移到另一幅图像上。它可以将著名绘画的风格应用到照片上。
  • 导航系统:Pix2Pix在导航系统中有应用。我们可以捕捉街景图像,并使用Pix2Pix将其转换为准确的地图。对于自主导航系统来说,这非常有价值。
  • 医学影像:Pix2Pix可以增强和转换医学影像。对于提供更好的治疗,高分辨率的影像在医疗行业中总是很有帮助的。Pix2Pix能够将低分辨率的MRI扫描转换为高分辨率的扫描,或者从X射线图像生成CT图像。
  • 艺术和创造力:可以将Pix2Pix用于创意目的。它根据用户的输入生成独特的艺术图像或动画。

使用Pix2Pix的公司

现在让我们来看看一些使用Pix2Pix的公司。

  • Adobe使用Pix2Pix开发其创意云产品的功能。其中包括将草图转换为逼真的图像,以及将图像从一种风格转换为另一种风格。Adobe还使用Pix2Pix为其机器学习模型生成合成数据。
  • Google使用Pix2Pix开发地图和照片产品的功能。它可以从卫星图像创建逼真的街景图,并将黑白照片上色。
  • Nvidia在其人工智能平台中使用Pix2Pix。它能够为机器学习模型生成合成数据集,并创建新的图像风格。
  • Google的Magenta Studio是一个探索机器学习和艺术的研究项目。Google的Magenta Studio使用Pix2Pix创建了许多艺术创作工具。Magenta Studio发布了许多使用Pix2Pix创建不同类型艺术的Colab笔记本。其中包括图像转换、图像补全和图像修复等。图像修复包括从图像中删除对象或填充图像的缺失部分。Magenta Studio还发布了许多使用Pix2Pix生成各种艺术形式的Magenta模型。这些模型包括Pix2PixHD(将低分辨率图像生成高分辨率图像)、Disco Diffusion(创建受各种艺术风格启发的图像)和GANPaint(产生将真实性与想象力融合的图像)。

实施

让我们首先导入所有必要的库和模块。如果发现任何缺失的模块,请使用pip命令导入它们。

import numpy as npfrom matplotlib import pylab as pltimport cv2import tensorflow as tfimport tensorflow.keras.layers as layersfrom tensorflow.keras.models import Modelfrom glob import globimport timeimport os

数据集

我们在这个项目中使用的数据集可以在Kaggle上找到,您可以从这里下载。

链接:https://www.kaggle.com/datasets/balraj98/facades-dataset

这个数据集包含建筑立面图片及其对应的分割图片。它被分为训练和测试子集。总共有506张建筑立面图片。

来源:Kaggle

预处理

我们的下一步是加载数据并根据问题陈述进行预处理。我们将定义一个函数来执行所有必要的步骤。它加载图像和对应的标签的批次,对它们进行预处理,并将它们作为NumPy数组返回,准备好输入模型。首先,我们指定测试图片和测试标签所在的路径。它使用glob函数在两个目录中找到所有文件。创建两个空列表,img_A和img_B。这些空列表将存储来自批次1和2的预处理图像。创建循环后,它遍历批次1和2中文件路径的成对。对于每一对,使用openCV读取图像并将它们存储在变量中。

颜色通道

我们翻转图像的颜色通道,这是对齐深度学习模型输入规范常常需要的一步。然后,我们将图像调整大小为256×256像素,最后,我们将预处理后的图像添加到它们各自的列表中。在处理完批量中的所有图像之后,代码将列表img_A和img_B转换为NumPy数组,并将像素值缩放到[-1, 1]的范围。最后,它将处理后的图像作为img_A和img_B返回。

def load_data(batch_size): path1 = sorted(glob('../test_picture/*')) path2 = sorted(glob('../test_label/*')) i = np.random.randint(0, 27) batch1 = path1[i * batch_size:(i + 1) * batch_size] batch2 = path2[i * batch_size:(i + 1) * batch_size] img_A = [] img_B = [] for filename1, filename2 in zip(batch1, batch2): img1 = cv2.imread(filename1) img2 = cv2.imread(filename2) img1 = img1[...,::-1] img2 = img2[...,::-1] img1 = cv2.resize(img1, (256,256), interpolation=cv2.INTER_AREA) img2 = cv2.resize(img2, (256,256), interpolation=cv2.INTER_AREA) img_A.append(img1) img_B.append(img2) img_A = np.array(img_A) / 127.5 - 1 img_B = np.array(img_B) / 127.5 - 1 return img_A, img_B 

类似地,我们需要创建另一个函数来为训练数据进行相同的操作。之前,我们已经对测试数据完成了所有的预处理步骤,最后将所有的图像保存在了列表中,并且它们一直存在到最后。但是对于训练数据的预处理,我们不需要将它们全部保存到最后。所以,我们使用了生成器函数。yield语句用于创建生成器函数。它为当前批次返回处理后的图像作为img_A和img_B,使您能够一次加载一个批次的训练数据而不是一次加载到内存中。这是生成器的优点。

# 生成器函数def load_batch(batch_size): path1 = sorted(glob('../train_picture/*')) path2 = sorted(glob('../train_label/*')) n_batches = int(len(path1) / batch_size) for i in range(n_batches): batch1 = path1[i * batch_size:(i + 1) * batch_size] batch2 = path2[i * batch_size:(i + 1) * batch_size] img_A, img_B = [], [] for filename1, filename2 in zip(batch1, batch2): img1 = cv2.imread(filename1) img2 = cv2.imread(filename2) img1 = img1[...,::-1] img2 = img2[...,::-1] img1 = cv2.resize(img1, (256,256), interpolation=cv2.INTER_AREA) img2 = cv2.resize(img2, (256,256), interpolation=cv2.INTER_AREA) img_A.append(img1) img_B.append(img2) img_A = np.array(img_A) / 127.5 - 1 img_B = np.array(img_B) / 127.5 - 1 yield img_A, img_B 

接下来,我们将定义一个名为pix2pix的类,在其中定义所需的所有函数。我们将定义一个构造方法(constructor),生成器(generator),鉴别器(discriminator),训练方法(train)和sample_images(用于可视化输出的方法)。我们将详细学习每个方法。

class pix2pix():    def __init__(self):      pass    def build_generator(self):      pass    def build_discriminator(self):      pass    def train(self, epochs, batch_size=1):      pass    def sample_images(self, epoch):      pass     

构造方法

首先,我们将定义构造方法。该方法初始化pix2pix模型的属性和组件。它是一个独特的方法,在创建类的对象时会自动被调用。我们已经定义了图像的维度和通道数。预期图像是256×256像素,具有3个颜色通道(RGB)。self.gf和self.df是定义生成器和鉴别器模型的滤波器(通道)数量的属性。

接下来,我们将定义一个优化器,在模型训练中使用具有特定学习率和beta参数的Adam优化器。接下来,创建鉴别器模型。它配置了二进制交叉熵损失和之前定义的Adam优化器。我们还在组合模型的训练过程中冻结了鉴别器的权重。self.combined属性表示组合模型,由生成器后接鉴别器组成。生成器生成假图像,而鉴别器决定它们的有效性。这个组合模型训练生成器以生成更加逼真的图像。

def __init__(self):        self.img_rows=256        self.img_cols=256        self.channels=3        self.img_shape=(self.img_rows,self.img_cols,self.channels)            patch=int(self.img_rows/(2**4)) # 2**4 = 16        self.disc_patch=(patch,patch,1)            self.gf=64        self.df=64            optimizer=tf.keras.optimizers.legacy.Adam(learning_rate=0.0002, beta_1=0.5)            self.discriminator=self.build_discriminator()        #self.discriminator.summary()        self.discriminator.compile(loss='binary_crossentropy', optimizer=optimizer)            self.generator=self.build_generator()        #self.generator.summary()            img_A=layers.Input(shape=self.img_shape)#图片--标签        img_B=layers.Input(shape=self.img_shape)#标签--真实        img=self.generator(img_A)            self.discriminator.trainable=False            valid=self.discriminator([img,img_A])            self.combined=Model(img_A,valid)        self.combined.compile(loss='binary_crossentropy', optimizer=optimizer)

构建生成器

接下来,我们要构建一个生成器。这个方法定义了生成器模型的架构,以pix2pix风格的GAN为基础。在这里,我们需要两个不同的函数。它们是conv2d和deconv2d。conv2d是一个帮助函数,用于创建一个具有可选批量归一化的卷积层。它接受输入张量、通道数、内核大小和bn(一个布尔值,表示是否使用批量归一化)。它应用一个二维卷积、LeakyReLU激活函数和可选的批量归一化,并返回结果张量。

与conv2d类似,这也是一个帮助函数,用于创建一个具有可选的dropout和batch normalization的逆卷积层(也称为上采样层)。它接受一个输入张量、一个来自上一层的输入张量,要连接的通道数、内核大小和dropout率。它应用一个上采样层、卷积、激活、dropout(如果指定了)和批量归一化,然后与skip_input进行连接,并返回结果张量。

生成器模型由多个层组成,从输入层开始。接下来,经过一系列的卷积(conv2d)和反卷积(deconv2d)层。在这里,d1到d7是逐渐减小尺寸并增加通道数的卷积层。同样地,u1到u7是逐渐增加尺寸并减少通道数的反卷积层。skip连接有助于保留从输入图像到输出的细节,使其适用于pix2pix框架中的图像转换等任务。最后一层是具有tanh激活函数的卷积层。这产生输出图像。它具有与输入图像相同的通道数(self.channels),旨在生成类似目标域的图像。

def build_generator(self):        def conv2d(layer_input,filters,f_size=(4,4),bn=True):            d=layers.Conv2D(filters,kernel_size=f_size,strides=(2,2),            padding='same')(layer_input)            d=layers.LeakyReLU(0.2)(d)            if bn:                d=layers.BatchNormalization()(d)            return d            def deconv2d(layer_input,skip_input,filters,f_size=(4,4),dropout_rate=0):            u=layers.UpSampling2D((2,2))(layer_input)            u=layers.Conv2D(filters,kernel_size=f_size,strides=(1,1),            padding='same',activation='relu')(u)            if dropout_rate:                u=layers.Dropout(dropout_rate)(u)            u=layers.BatchNormalization()(u)            u=layers.Concatenate()([u,skip_input])            return u            d0=layers.Input(shape=self.img_shape)            d1=conv2d(d0,self.gf,bn=False)         d2=conv2d(d1,self.gf*2)                 d3=conv2d(d2,self.gf*4)                 d4=conv2d(d3,self.gf*8)                 d5=conv2d(d4,self.gf*8)                 d6=conv2d(d5,self.gf*8)                    d7=conv2d(d6,self.gf*8)                     u1=deconv2d(d7,d6,self.gf*8,dropout_rate=0.5)           u2=deconv2d(u1,d5,self.gf*8,dropout_rate=0.5)           u3=deconv2d(u2,d4,self.gf*8,dropout_rate=0.5)           u4=deconv2d(u3,d3,self.gf*4)           u5=deconv2d(u4,d2,self.gf*2)           u6=deconv2d(u5,d1,self.gf)             u7=layers.UpSampling2D((2,2))(u6)            output_img=layers.Conv2D(self.channels,kernel_size=(4,4),strides=(1,1),        padding='same',activation='tanh')(u7)            return Model(d0,output_img)

构建鉴别器

我们的下一步是构建一个鉴别器模型。这个方法定义了一个pix2pix风格GAN中鉴别器模型的架构。与生成器中的conv2d函数类似,我们在这里定义d_layer函数。这个辅助函数创建一个带有可选批归一化的卷积层。它接受输入张量、通道数、卷积核大小以及bn,一个表示是否使用批归一化的布尔值。它应用了一个二维卷积、LeakyReLU激活以及可选批归一化,并返回结果张量。鉴别器模型有两个输入层,img_A和img_B,每个都有一个由self.img_shape定义的形状。

这些输入表示一对图像:一个来自源域(img_A),一个来自目标域(img_B)。输入图像img_A和img_B在通道轴(轴=-1)上进行串联,形成组合图像。鉴别器的架构由卷积层d1到d4组成,滤波器数量逐渐增加。这些层在提取特征的同时降低了输入图像的空间维度。最后一层是一个带有sigmoid激活函数的卷积层。它产生一个表示输入图像对是真实还是伪造的单通道输出。使用这个输出来对输入图像对进行分类,判断它们是真实的还是伪造的。

def build_discriminator(self):        def d_layer(layer_input,filters,f_size=(4,4),bn=True):            d=layers.Conv2D(filters,kernel_size=f_size,strides=(2,2),            padding='same')(layer_input)            d=layers.LeakyReLU(0.2)(d)            if bn:                d=layers.BatchNormalization()(d)            return d            img_A=layers.Input(shape=self.img_shape)        img_B=layers.Input(shape=self.img_shape)            combined_imgs=layers.Concatenate(axis=-1)([img_A,img_B])            d1=d_layer(combined_imgs,self.df,bn=False)        d2=d_layer(d1,self.df*2)        d3=d_layer(d2,self.df*4)        d4=d_layer(d3,self.df*8)            validity=layers.Conv2D(1,kernel_size=(4,4),strides=(1,1),padding='same',        activation='sigmoid')(d4)            return Model([img_A,img_B],validity)

训练

我们需要创建训练方法,以便在调用时对模型进行训练。”valid”数组由numpy数组形式的一表示真实图像的标签组成。同样,”fake”数组由numpy数组形式的零表示伪造(生成的)图像的标签。随后,我们初始化一个循环,迭代指定数量的epochs。在每个epoch中,我们启动一个计时器来记录该特定epoch所花费的时间。生成器被用来按批次加载训练数据,每个epoch内产生图像对img_A(输入)和img_B(目标)。

生成器利用输入图像生成图像。鉴别器训练以将真实图像对分类为真实,计算真实图像的损失。同样,鉴别器训练以将生成图像对分类为伪造,随后计算伪造图像的损失。总鉴别器损失由真实和伪造图像的损失平均值确定。生成器的训练目标是生成能够欺骗鉴别器将其分类为真实的图像。

def train(self,epochs,batch_size=1):        valid=np.ones((batch_size,)+self.disc_patch)        fake=np.zeros((batch_size,)+self.disc_patch)            for epoch in range(epochs):            start=time.time()            for batch_i,(img_A,img_B) in enumerate(load_batch(1)):                gen_imgs=self.generator.predict(img_A)                        d_loss_real = self.discriminator.train_on_batch([img_B, img_A], valid)                d_loss_fake = self.discriminator.train_on_batch([gen_imgs, img_A], fake)                d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)                        g_loss = self.combined.train_on_batch(img_A,valid)                if batch_i % 500 == 0:                    print ("[Epoch %d] [Batch %d] [D loss: %f] [G loss: %f]" % (epoch,batch_i,                                                                                d_loss,g_loss))                        self.sample_images(epoch)            print('Time for epoch {} is {} sec'.format(epoch,time.time()-start))

可视化

sample_images方法生成并显示样本图像,以可视化生成器在训练过程中的进展。这里将r和c设置为3,表示显示的图像网格将有3行3列。加载了3对输入和目标图像。利用生成器基于输入图像生成伪造图像。然后将图像串联成单个数组以进行显示。像素值从范围[-1, 1]重新缩放到[0, 1]以进行正确的可视化。图像显示在子图中。将该图像保存为以epoch号命名的图像文件。

def sample_images(self, epoch):        r, c = 3, 3        img_A, img_B =load_data(3)        fake_A = self.generator.predict(img_A)        gen_imgs = np.concatenate([img_A, fake_A, img_B])        # 将图片的值缩放到0-1之间        gen_imgs = 0.5 * gen_imgs + 0.5        titles = ['输入图像', '预测图像', '真实图像']        fig, axs = plt.subplots(r, c)        cnt = 0        for i in range(r):            for j in range(c):                axs[i,j].imshow(gen_imgs[cnt])                axs[i,j].set_title(titles[i])                axs[i,j].axis('off')                cnt += 1        fig.savefig("./%d.png" % (epoch))        plt.show()

结果

在定义完所有必需的方法之后,必须调用主方法。创建一个名为gan的pip2pix类的对象。然后,通过指定epochs的数量和batch_size来训练模型。

每个epoch结束后,预测的图像将与输入图像和真实图像一起显示。随着训练的进行,您可以观察图片的变化。随着epochs数量的增加,图像将变得更加精确。最终,您将获得与真实图像无法区分的图像。这就是GAN的力量。

if __name__ == '__main__':
    gan = pix2pix()
    gan.train(epochs=50, batch_size=1)

第1个epoch的结果:

经过10个epoch后,结果如下:

50个epoch后的结果是:

结论

Pix2Pix的成功在于它能够从数据中学习并生成不仅逼真而且富有艺术表现力的图像。无论是将白天场景转换为夜晚场景,还是将黑白照片转换为鲜艳的色彩,Pix2Pix都已经证明了其能力。通过允许艺术家和设计师以创新和富有想象力的方式转换和操纵图像,Pix2Pix已成为一种创造性的超能力。随着技术的不断进步,Pix2Pix将带来更多令人惊叹的机会。对于对艺术和人工智能相结合感兴趣的人来说,这是一个令人兴奋的领域。

核心要点

  • Pix2Pix是一个聪明的计算机朋友,可以帮助我们从想法中制作出精美的图片。它就像是数字世界的魔术!
  • Pix2Pix已成为计算机视觉和图像处理方面的一项革命性技术。
  • 它提供了令人兴奋的可能性,但也面临一些挑战,例如训练的稳定性和对大量数据集的需求。
  • 谷歌的Magenta Studio是一个研究项目,探索机器学习和艺术,它使用Pix2Pix创建了不同的艺术制作工具。
  • 在本文中,我们看到了pix2pix的工作原理,并理解了它的神奇力量。
  • 我们学会了如何使用Pix2Pix来将建筑立面数据转换为真实的建筑图片,从而获得了实际的理解。

常见问题

本文中显示的媒体不归Analytics Vidhya所有,仅供作者自行决定使用。