欢迎您的访问
专注架构,Java,数据结构算法,Python技术分享

基于Windows11系统和NVIDIA显卡的DeepNude无水印版本零基础复现教程

引言

作为2023年最热门的计算机视觉(CV)项目之一,DeepNude引起了非常多技术发烧友和普通大众更大关注。这个项目使用MASK技术实现了人体图像特定遮挡物的去除与去除后的图像生成,采用了对抗神经网络(GAN)等多种值得学习的计算机视觉(CV)技术,值得学习。因此俺写了这篇DeepNude在普通人的Windows电脑上进行复现的教程。

第一步:获得项目代码和模型

项目的代码和模型俺已经都准备好了,需要的小伙伴可以加入最简化机器学习获得。

DeepNude DeepNudeDeepNude 虽然应用程序已经下架,但其原始算法仍然在 GitHub 上公开,其中的算法值得研究。DeepNude DeepNudeDeepNude 的核心技术基于 Conditional GAN(CGAN)和 pix2pixHD,我们首先查阅整理这两个技术的关键:

  1. Conditional GAN(CGAN):GAN 的训练目标是生成逼真图片,但无法控制生成的内容,所以在实用性上有很大的限制。为了控制 GAN 的生成内容,Mizra 提出了 Conditional GAN (CGAN) 来解决这个问题。CGAN 的改造其实很简单易懂:把控制变量(label)与 latent variable 合并。这样,CGAN 的输入就有了人为可理解的意义,因为 label 都是人为定义的——例如在人脸生成中,label 可以包含年龄、性别、表情等等控制变量。CGAN 的设计,让人类可以更直观的控制 GAN 的生成内容。
  2. pix2pixHD:pix2pixHD 是由 NVIDIA 提出的高清图片生成算法,它解决了生成高清图片的问题。pix2pixHD 的网络结构分为两部分:G1 和 G2。G1 是全局的生成网络,可以在一半的图片大小上完成图片的转换。G2 是局部增强网络,可以把 G1 的输出放大回原来的图片大小并确保细节。这种设计避免了计算资源过大的问题——大部分的运算是在较低解析度的 G1 完成,替高解析度 G2 分担了大量的运算消耗。

二者的关系是:DeepNude DeepNudeDeepNude 的算法使用了 CGAN 作为背后的核心概念。但是 CGAN 仍然有一些未解决的问题,例如生成高清图片。这个问题通过 pix2pixHD 得到了解决。

DeepNude DeepNudeDeepNude 的实际做法是将问题拆解成三个部分。第一步先生成大致的 Label Map (Mask),第二步生成精细的 LabelMap (MaskDet),第三步生成果体图 (Nude)。每一步都经过了 OpenCV 前处理与 GAN 生成两步骤。

DeepNude DeepNudeDeepNude 的工作流程:

  1. 输入:用户提供一张人像照片。
  2. OpenCV 前处理:使用 OpenCV 对输入的照片进行预处理,包括裁剪、缩放等操作,以便于后续的图像生成。
  3. 生成大致的 Label Map (Mask):使用 Conditional GAN (CGAN) 生成一个大致的 Label Map,这是一个粗略的人体轮廓图。
  4. 生成精细的 LabelMap (MaskDet):在大致的 Label Map 的基础上,使用 CGAN 生成一个更精细的 Label Map,这是一个更详细的人体轮廓图。
  5. 生成裸体图 (Nude):最后,使用 pix2pixHD 算法,根据精细的 Label Map 生成最终的裸体图。
  6. 输出:将生成的裸体图返回给用户。

据此我们重新进行测试性训练和预测生成,效果一言难尽…..我们就把全部代码、模型和测试日志放在了知识星球上面

部分代码如下:

  1. class InstanceNormalization(tf.keras.layers.Layer):

  2. “””Instance Normalization Layer (https://arxiv.org/abs/1607.08022).”””

  3. def __init__(self, epsilon=1e-5):

  4. super(InstanceNormalization, self).__init__()

  5. self.epsilon = epsilon

  6. def build(self, input_shape):

  7. self.scale = self.add_weight(

  8. name=’scale’,

  9. shape=input_shape[-1:],

  10. initializer=tf.random_normal_initializer(0., 0.02),

  11. trainable=True)

  12. self.offset = self.add_weight(

  13. name=’offset’,

  14. shape=input_shape[-1:],

  15. initializer=’zeros’,

  16. trainable=True)

  17. mean, variance = tf.nn.moments(x, axes=[1, 2], keepdims=True)

  18. inv = tf.math.rsqrt(variance + self.epsilon)

  19. normalized = (x – mean) * inv

  20. return self.scale * normalized + self.offset

  21. def downsample(filters, size, norm_type=’batchnorm’, apply_norm=True):

  22. “””Downsamples an input.

  23. Conv2D => Batchnorm => LeakyRelu

  24. filters: number of filters

  25. norm_type: Normalization type; either ‘batchnorm’ or ‘instancenorm’.

  26. apply_norm: If True, adds the batchnorm layer

  27. Downsample Sequential Model

  28. initializer = tf.random_normal_initializer(0., 0.02)

  29. result = tf.keras.Sequential()

  30. tf.keras.layers.Conv2D(filters, size, strides=2, padding=’same’,

  31. kernel_initializer=initializer, use_bias=False))

  32. if norm_type.lower() == ‘batchnorm’:

  33. result.add(tf.keras.layers.BatchNormalization())

  34. elif norm_type.lower() == ‘instancenorm’:

  35. result.add(InstanceNormalization())

  36. result.add(tf.keras.layers.LeakyReLU())

  37. def upsample(filters, size, norm_type=’batchnorm’, apply_dropout=False):

  38. “””Upsamples an input.

  39. Conv2DTranspose => Batchnorm => Dropout => Relu

  40. filters: number of filters

  41. norm_type: Normalization type; either ‘batchnorm’ or ‘instancenorm’.

  42. apply_dropout: If True, adds the dropout layer

  43. Upsample Sequential Model

  44. initializer = tf.random_normal_initializer(0., 0.02)

  45. result = tf.keras.Sequential()

  46. tf.keras.layers.Conv2DTranspose(filters, size, strides=2,

  47. padding=’same’,

  48. kernel_initializer=initializer,

  49. use_bias=False))

  50. if norm_type.lower() == ‘batchnorm’:

  51. result.add(tf.keras.layers.BatchNormalization())

  52. elif norm_type.lower() == ‘instancenorm’:

  53. result.add(InstanceNormalization())

  54. result.add(tf.keras.layers.Dropout(0.5))

  55. result.add(tf.keras.layers.ReLU())

  56. def unet_generator(output_channels, norm_type=’batchnorm’):

  57. “””Modified u-net generator model (https://arxiv.org/abs/1611.07004).

  58. output_channels: Output channels

  59. norm_type: Type of normalization. Either ‘batchnorm’ or ‘instancenorm’.

  60. downsample(64, 4, norm_type, apply_norm=False), # (bs, 128, 128, 64)

  61. downsample(128, 4, norm_type), # (bs, 64, 64, 128)

  62. downsample(256, 4, norm_type), # (bs, 32, 32, 256)

  63. downsample(512, 4, norm_type), # (bs, 16, 16, 512)

  64. downsample(512, 4, norm_type), # (bs, 8, 8, 512)

  65. downsample(512, 4, norm_type), # (bs, 4, 4, 512)

  66. downsample(512, 4, norm_type), # (bs, 2, 2, 512)

  67. downsample(512, 4, norm_type), # (bs, 1, 1, 512)

  68. upsample(512, 4, norm_type, apply_dropout=True), # (bs, 2, 2, 1024)

  69. upsample(512, 4, norm_type, apply_dropout=True), # (bs, 4, 4, 1024)

  70. upsample(512, 4, norm_type, apply_dropout=True), # (bs, 8, 8, 1024)

  71. upsample(512, 4, norm_type), # (bs, 16, 16, 1024)

  72. upsample(256, 4, norm_type), # (bs, 32, 32, 512)

  73. upsample(128, 4, norm_type), # (bs, 64, 64, 256)

  74. upsample(64, 4, norm_type), # (bs, 128, 128, 128)

  75. initializer = tf.random_normal_initializer(0., 0.02)

  76. last = tf.keras.layers.Conv2DTranspose(

  77. output_channels, 4, strides=2,

  78. padding=’same’, kernel_initializer=initializer,

  79. activation=’tanh’) # (bs, 256, 256, 3)

  80. concat = tf.keras.layers.Concatenate()

  81. inputs = tf.keras.layers.Input(shape=[None, None, 3])

  82. # Downsampling through the model

  83. for down in down_stack:

  84. skips = reversed(skips[:-1])

  85. # Upsampling and establishing the skip connections

  86. for up, skip in zip(up_stack, skips):

  87. x = concat([x, skip])

  88. return tf.keras.Model(inputs=inputs, outputs=x)

  89. def discriminator(norm_type=’batchnorm’, target=True):

  90. “””PatchGan discriminator model (https://arxiv.org/abs/1611.07004).

  91. norm_type: Type of normalization. Either ‘batchnorm’ or ‘instancenorm’.

  92. target: Bool, indicating whether target image is an input or not.

  93. Discriminator model

  94. initializer = tf.random_normal_initializer(0., 0.02)

  95. inp = tf.keras.layers.Input(shape=[None, None, 3], name=’input_image’)

  96. tar = tf.keras.layers.Input(shape=[None, None, 3], name=’target_image’)

  97. x = tf.keras.layers.concatenate([inp, tar]) # (bs, 256, 256, channels*2)

  98. down1 = downsample(64, 4, norm_type, False)(x) # (bs, 128, 128, 64)

  99. down2 = downsample(128, 4, norm_type)(down1) # (bs, 64, 64, 128)

  100. down3 = downsample(256, 4, norm_type)(down2) # (bs, 32, 32, 256)

  101. zero_pad1 = tf.keras.layers.ZeroPadding2D()(down3) # (bs, 34, 34, 256)

  102. conv = tf.keras.layers.Conv2D(

  103. 512, 4, strides=1, kernel_initializer=initializer,

  104. use_bias=False)(zero_pad1) # (bs, 31, 31, 512)

  105. if norm_type.lower() == ‘batchnorm’:

  106. norm1 = tf.keras.layers.BatchNormalization()(conv)

  107. elif norm_type.lower() == ‘instancenorm’:

  108. norm1 = InstanceNormalization()(conv)

  109. leaky_relu = tf.keras.layers.LeakyReLU()(norm1)

  110. zero_pad2 = tf.keras.layers.ZeroPadding2D()(leaky_relu) # (bs, 33, 33, 512)

  111. last = tf.keras.layers.Conv2D(

  112. kernel_initializer=initializer)(zero_pad2) # (bs, 30, 30, 1)

  113. return tf.keras.Model(inputs=[inp, tar], outputs=last)

  114. return tf.keras.Model(inputs=inp, outputs=last)

  115. loss_obj = tf.keras.losses.BinaryCrossentropy(from_logits=True)

  116. def discriminator_loss(real, generated):

  117. real_loss = loss_obj(tf.ones_like(real), real)

  118. generated_loss = loss_obj(tf.zeros_like(generated), generated)

  119. total_disc_loss = real_loss + generated_loss

  120. return total_disc_loss * 0.5

  121. def generator_loss(generated):

  122. return loss_obj(tf.ones_like(generated), generated)

  123. def calc_cycle_loss(real_image, cycled_image):

  124. loss1 = tf.reduce_mean(tf.abs(real_image – cycled_image))

  125. return LAMBDA * loss1

  126. def identity_loss(real_image, same_image):

  127. loss = tf.reduce_mean(tf.abs(real_image – same_image))

  128. return LAMBDA * 0.5 * loss

  129. if __name__ == “__main__”:

  130. generator_g = unet_generator(OUTPUT_CHANNELS, norm_type=’instancenorm’)

  131. generator_f = unet_generator(OUTPUT_CHANNELS, norm_type=’instancenorm’)

  132. discriminator_x = discriminator(norm_type=’instancenorm’, target=False)

  133. discriminator_y = discriminator(norm_type=’instancenorm’, target=False)

  134. sample_apple = tf.random.normal([BATCH_SIZE, IMG_HEIGHT, IMG_WIDTH, INPUT_CHANNELS])

  135. sample_orange = tf.random.normal([BATCH_SIZE, IMG_HEIGHT, IMG_WIDTH, INPUT_CHANNELS])

  136. print(f”Inputs sample_apple.shape {sample_apple.shape}”)

  137. print(f”Inputs sample_orange.shape {sample_orange.shape}”)

  138. print(f”Pass by —————–generator_g———————-“)

  139. print(f”Pass by —————–generator_f———————-“)

  140. to_orange = generator_g(sample_apple)

  141. to_apple = generator_f(sample_orange)

  142. print(f”Outputs to_orange.shape {to_orange.shape}”)

  143. print(f”Outputs to_apple.shape {to_apple.shape}”)

  144. print(f”Inputs sample_apple.shape {sample_apple.shape}”)

  145. print(f”Inputs sample_orange.shape {sample_orange.shape}”)

  146. print(f”Pass by —————–discriminator_y———————-“)

  147. print(f”Pass by —————–discriminator_x———————-“)

  148. disc_real_orange = discriminator_y(sample_orange)

  149. disc_real_apple = discriminator_x(sample_apple)

  150. print(f”Outputs disc_real_orange.shape {disc_real_orange.shape}”)

  151. print(f”Outputs disc_real_apple.shape {disc_real_apple.shape}”)

 

赞(0) 打赏
版权归原创作者所有,任何形式转载请联系作者;码农code之路 » 基于Windows11系统和NVIDIA显卡的DeepNude无水印版本零基础复现教程

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏