text: import torch
  import torch.nn as nn
  from torch.nn import init
  import functools
  import math
  from torch.autograd import Variable
  import numpy as np
  ###############################################################################
  # Functions
  ###############################################################################
  
  
  def weights_init(m):
      classname = m.__class__.__name__
      if classname.find('Conv') != -1:
          m.weight.data.normal_(0.0, 0.02)
      elif classname.find('BatchNorm2d') != -1:
          m.weight.data.normal_(1.0, 0.02)
          m.bias.data.fill_(0)
  
  
  def get_norm_layer(norm_type='instance'):
      if norm_type == 'batch':
          norm_layer = functools.partial(nn.BatchNorm2d, affine=True)
      elif norm_type == 'instance':
          norm_layer = functools.partial(nn.InstanceNorm2d, affine=False)
      else:
          raise NotImplementedError('normalization layer [%s] is not found' % norm)
      return norm_layer
  
  
  def define_G(input_nc, output_nc, ngf, which_model_netG, norm='batch', use_dropout=False, gpu_ids=[]):
      netG = None
      use_gpu = len(gpu_ids) > 0
      norm_layer = get_norm_layer(norm_type=norm)
  
      if use_gpu:
          assert(torch.cuda.is_available())
  
      if which_model_netG == 'resnet_9blocks':
          netG = ResnetGenerator(input_nc, output_nc, ngf, norm_layer=norm_layer, use_dropout=use_dropout, n_blocks=9, gpu_ids=gpu_ids)
      elif which_model_netG == 'resnet_6blocks':
          netG = ResnetGenerator(input_nc, output_nc, ngf, norm_layer=norm_layer, use_dropout=use_dropout, n_blocks=6, gpu_ids=gpu_ids)
      elif which_model_netG == 'unet_128':
          netG = UnetGenerator(input_nc, output_nc, 7, ngf, norm_layer=norm_layer, use_dropout=use_dropout, gpu_ids=gpu_ids)
      elif which_model_netG == 'unet_256':
          netG = UnetGenerator(input_nc, output_nc, 8, ngf, norm_layer=norm_layer, use_dropout=use_dropout, gpu_ids=gpu_ids)
      else:
          raise NotImplementedError('Generator model name [%s] is not recognized' % which_model_netG)
      if len(gpu_ids) > 0:
          netG.cuda(device=gpu_ids[0])
      netG.apply(weights_init)
      return netG
  
  
  def define_image_D(input_nc, ndf, which_model_netD,
               n_layers_D=3, norm='batch', use_sigmoid=False, gpu_ids=[]):
      netD = None
      use_gpu = len(gpu_ids) > 0
      norm_layer = get_norm_layer(norm_type=norm)
  
      if use_gpu:
          assert(torch.cuda.is_available())
      if which_model_netD == 'basic':
          netD = NLayerDiscriminator(input_nc, ndf, n_layers=3, norm_layer=norm_layer, use_sigmoid=use_sigmoid, gpu_ids=gpu_ids)
      elif which_model_netD == 'n_layers':
          netD = NLayerDiscriminator(input_nc, ndf, n_layers_D, norm_layer=norm_layer, use_sigmoid=use_sigmoid, gpu_ids=gpu_ids)
      else:
          raise NotImplementedError('Discriminator model name [%s] is not recognized' %
                                    which_model_netD)
      if use_gpu:
          netD.cuda(device=gpu_ids[0])
      netD.apply(weights_init)
      return netD
  
  def define_person_D(input_nc, ndf, opt, use_sigmoid=False, gpu_ids=[]):
      netD = None
      use_gpu = len(gpu_ids) > 0
      # print('haha')
      # print(use_sigmoid)
  
      if use_gpu:
          assert(torch.cuda.is_available())
      if (opt.use_spp == False):
          netD = PersonDiscriminator(input_nc, ndf, gpu_ids=gpu_ids)
      else:
          netD = SPP_NET( opt, input_nc, ndf, use_sigmoid, gpu_ids=gpu_ids)
  
      if use_gpu:
          netD.cuda()
      netD.apply(weights_init)
      return netD
  
  
  def print_network(net):
      num_params = 0
      for param in net.parameters():
          num_params += param.numel()
      print(net)
      print('Total number of parameters: %d' % num_params)
  
  
  ##############################################################################
  # Classes
  ##############################################################################
  
  
  # Defines the GAN loss which uses either LSGAN or the regular GAN.
  # When LSGAN is used, it is basically same as MSELoss,
  # but it abstracts away the need to create the target label tensor
  # that has the same size as the input
  class GANLoss(nn.Module):
      def __init__(self, use_lsgan=True, target_real_label=1.0, target_fake_label=0.0,
                   tensor=torch.FloatTensor):
          super(GANLoss, self).__init__()
          self.real_label = target_real_label
          self.fake_label = target_fake_label
          self.real_label_var = None
          self.fake_label_var = None
          self.Tensor = tensor
          print(use_lsgan)
          if use_lsgan:
              self.loss = nn.MSELoss()
          else:
              self.loss = nn.BCELoss()
  
      def get_target_tensor(self, input, target_is_real):
          target_tensor = None
          if target_is_real:
              create_label = ((self.real_label_var is None) or
                              (self.real_label_var.numel() != input.numel()))
              if create_label:
                  real_tensor = self.Tensor(input.size()).fill_(self.real_label)
                  self.real_label_var = Variable(real_tensor, requires_grad=False)
              target_tensor = self.real_label_var
          else:
              create_label = ((self.fake_label_var is None) or
                              (self.fake_label_var.numel() != input.numel()))
              if create_label:
                  fake_tensor = self.Tensor(input.size()).fill_(self.fake_label)
                  self.fake_label_var = Variable(fake_tensor, requires_grad=False)
              target_tensor = self.fake_label_var
          return target_tensor
  
      def __call__(self, input, target_is_real):
          target_tensor = self.get_target_tensor(input, target_is_real)
          return self.loss(input, target_tensor)
  
  
  # Defines the generator that consists of Resnet blocks between a few
  # downsampling/upsampling operations.
  # Code and idea originally from Justin Johnson's architecture.
  # https://github.com/jcjohnson/fast-neural-style/
  class ResnetGenerator(nn.Module):
      def __init__(self, input_nc, output_nc, ngf=64, norm_layer=nn.BatchNorm2d, use_dropout=False, n_blocks=6, gpu_ids=[], padding_type='reflect'):
          assert(n_blocks >= 0)
          super(ResnetGenerator, self).__init__()
          self.input_nc = input_nc
          self.output_nc = output_nc
          self.ngf = ngf
          self.gpu_ids = gpu_ids
  
          model = [nn.ReflectionPad2d(3),
                   nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0),
                   norm_layer(ngf),
                   nn.ReLU(True)]
  
          n_downsampling = 2
          for i in range(n_downsampling):
              mult = 2**i
              model += [nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3,
                                  stride=2, padding=1),
                        norm_layer(ngf * mult * 2),
                        nn.ReLU(True)]
  
          mult = 2**n_downsampling
          for i in range(n_blocks):
              model += [ResnetBlock(ngf * mult, padding_type=padding_type, norm_layer=norm_layer, use_dropout=use_dropout)]
  
          for i in range(n_downsampling):
              mult = 2**(n_downsampling - i)
              model += [nn.ConvTranspose2d(ngf * mult, int(ngf * mult / 2),
                                           kernel_size=3, stride=2,
                                           padding=1, output_padding=1),
                        norm_layer(int(ngf * mult / 2)),
                        nn.ReLU(True)]
          model += [nn.ReflectionPad2d(3)]
          model += [nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0)]
          model += [nn.Tanh()]
  
          self.model = nn.Sequential(*model)
  
      def forward(self, input):
          if self.gpu_ids and isinstance(input.data, torch.cuda.FloatTensor):
              return nn.parallel.data_parallel(self.model, input, self.gpu_ids)
          else:
              return self.model(input)
  
  
  # Define a resnet block
  class ResnetBlock(nn.Module):
      def __init__(self, dim, padding_type, norm_layer, use_dropout):
          super(ResnetBlock, self).__init__()
          self.conv_block = self.build_conv_block(dim, padding_type, norm_layer, use_dropout)
  
      def build_conv_block(self, dim, padding_type, norm_layer, use_dropout):
          conv_block = []
          p = 0
          if padding_type == 'reflect':
              conv_block += [nn.ReflectionPad2d(1)]
          elif padding_type == 'replicate':
              conv_block += [nn.ReplicationPad2d(1)]
          elif padding_type == 'zero':
              p = 1
          else:
              raise NotImplementedError('padding [%s] is not implemented' % padding_type)
  
          conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p),
                         norm_layer(dim),
                         nn.ReLU(True)]
          if use_dropout:
              conv_block += [nn.Dropout(0.5)]
  
          p = 0
          if padding_type == 'reflect':
              conv_block += [nn.ReflectionPad2d(1)]
          elif padding_type == 'replicate':
              conv_block += [nn.ReplicationPad2d(1)]
          elif padding_type == 'zero':
              p = 1
          else:
              raise NotImplementedError('padding [%s] is not implemented' % padding_type)
          conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p),
                         norm_layer(dim)]
  
          return nn.Sequential(*conv_block)
  
      def forward(self, x):
          out = x + self.conv_block(x)
          return out
  
  
  # Defines the Unet generator.
  # |num_downs|: number of downsamplings in UNet. For example,
  # if |num_downs| == 7, image of size 128x128 will become of size 1x1
  # at the bottleneck
  class UnetGenerator(nn.Module):
      def __init__(self, input_nc, output_nc, num_downs, ngf=64,
                   norm_layer=nn.BatchNorm2d, use_dropout=False, gpu_ids=[]):
          super(UnetGenerator, self).__init__()
          self.gpu_ids = gpu_ids
  
          # currently support only input_nc == output_nc
          assert(input_nc == output_nc)
  
          # construct unet structure
          unet_block = UnetSkipConnectionBlock(ngf * 8, ngf * 8, norm_layer=norm_layer, innermost=True)
          for i in range(num_downs - 5):
              unet_block = UnetSkipConnectionBlock(ngf * 8, ngf * 8, unet_block, norm_layer=norm_layer, use_dropout=use_dropout)
          unet_block = UnetSkipConnectionBlock(ngf * 4, ngf * 8, unet_block, norm_layer=norm_layer)
          unet_block = UnetSkipConnectionBlock(ngf * 2, ngf * 4, unet_block, norm_layer=norm_layer)
          unet_block = UnetSkipConnectionBlock(ngf, ngf * 2, unet_block, norm_layer=norm_layer)
          unet_block = UnetSkipConnectionBlock(output_nc, ngf, unet_block, outermost=True, norm_layer=norm_layer)
  
          self.model = unet_block
  
      def forward(self, input):
          if self.gpu_ids and isinstance(input.data, torch.cuda.FloatTensor):
              return nn.parallel.data_parallel(self.model, input, self.gpu_ids)
          else:
              return self.model(input)
  
  
  # Defines the submodule with skip connection.
  # X -------------------identity---------------------- X
  #   |-- downsampling -- |submodule| -- upsampling --|
  class UnetSkipConnectionBlock(nn.Module):
      def __init__(self, outer_nc, inner_nc,
                   submodule=None, outermost=False, innermost=False, norm_layer=nn.BatchNorm2d, use_dropout=False):
          super(UnetSkipConnectionBlock, self).__init__()
          self.outermost = outermost
  
          downconv = nn.Conv2d(outer_nc, inner_nc, kernel_size=4,
                               stride=2, padding=1)
          downrelu = nn.LeakyReLU(0.2, True)
          downnorm = norm_layer(inner_nc)
          uprelu = nn.ReLU(True)
          upnorm = norm_layer(outer_nc)
  
          if outermost:
              upconv = nn.ConvTranspose2d(inner_nc * 2, outer_nc,
                                          kernel_size=4, stride=2,
                                          padding=1)
              down = [downconv]
              up = [uprelu, upconv, nn.Tanh()]
              model = down + [submodule] + up
          elif innermost:
              upconv = nn.ConvTranspose2d(inner_nc, outer_nc,
                                          kernel_size=4, stride=2,
                                          padding=1)
              down = [downrelu, downconv]
              up = [uprelu, upconv, upnorm]
              model = down + up
          else:
              upconv = nn.ConvTranspose2d(inner_nc * 2, outer_nc,
                                          kernel_size=4, stride=2,
                                          padding=1)
              down = [downrelu, downconv, downnorm]
              up = [uprelu, upconv, upnorm]
  
              if use_dropout:
                  model = down + [submodule] + up + [nn.Dropout(0.5)]
              else:
                  model = down + [submodule] + up
  
          self.model = nn.Sequential(*model)
  
      def forward(self, x):
          if self.outermost:
              return self.model(x)
          else:
              return torch.cat([self.model(x), x], 1)
  
  
  # Defines the PatchGAN discriminator with the specified arguments.
  class NLayerDiscriminator(nn.Module):
      def __init__(self, input_nc, ndf=64, n_layers=3, norm_layer=nn.BatchNorm2d, use_sigmoid=False, gpu_ids=[]):
          super(NLayerDiscriminator, self).__init__()
          self.gpu_ids = gpu_ids
  
          kw = 4
          padw = int(np.ceil((kw-1)/2))
          sequence = [
              nn.Conv2d(input_nc, ndf, kernel_size=kw, stride=2, padding=padw),
              nn.LeakyReLU(0.2, True)
          ]
  
          nf_mult = 1
          nf_mult_prev = 1
          for n in range(1, n_layers):
              nf_mult_prev = nf_mult
              nf_mult = min(2**n, 8)
              sequence += [
                  nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult,
                            kernel_size=kw, stride=2, padding=padw),
                  norm_layer(ndf * nf_mult),
                  nn.LeakyReLU(0.2, True)
              ]
  
          nf_mult_prev = nf_mult
          nf_mult = min(2**n_layers, 8)
          sequence += [
              nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult,
                        kernel_size=kw, stride=1, padding=padw),
              norm_layer(ndf * nf_mult),
              nn.LeakyReLU(0.2, True)
          ]
  
          sequence += [nn.Conv2d(ndf * nf_mult, 1, kernel_size=kw, stride=1, padding=padw)]
  
          if use_sigmoid:
              sequence += [nn.Sigmoid()]
  
          self.model = nn.Sequential(*sequence)
  
      def forward(self, input):
          if len(self.gpu_ids) and isinstance(input.data, torch.cuda.FloatTensor):
              return nn.parallel.data_parallel(self.model, input, self.gpu_ids)
          else:
              return self.model(input)
  
  
  class PersonDiscriminator(nn.Module):
      def __init__(self, input_nc, ndf=64, gpu_ids=[]):
          super(PersonDiscriminator, self).__init__()
          self.gpu_ids = gpu_ids
          self.model = nn.Sequential(
              # input is (nc) x 64 x 64
              nn.Conv2d(input_nc, ndf, 4, 2, 1, bias=False),
              nn.LeakyReLU(0.2, inplace=True),
              # state size. (ndf) x 32 x 32
              nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
              nn.BatchNorm2d(ndf * 2),
              nn.LeakyReLU(0.2, inplace=True),
              # state size. (ndf*2) x 16 x 16
              nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
              nn.BatchNorm2d(ndf * 4),
              nn.LeakyReLU(0.2, inplace=True),
              # state size. (ndf*4) x 8 x 8
              nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),
              nn.BatchNorm2d(ndf * 8),
              nn.LeakyReLU(0.2, inplace=True),
              # state size. (ndf*8) x 4 x 4
              nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
              nn.Sigmoid()
          )
      def forward(self, input):
          if len(self.gpu_ids) and isinstance(input.data, torch.cuda.FloatTensor):
              return nn.parallel.data_parallel(self.model, input, self.gpu_ids)
          else:
              return self.model(input)
  
  class SPP_NET(nn.Module):
      def __init__(self, opt, input_nc, ndf=64, use_sigmoid=False, gpu_ids=[]):
          super(SPP_NET, self).__init__()
          self.gpu_ids = gpu_ids
          self.output_num = [4,2,1]
          
          self.conv1 = nn.Conv2d(input_nc, ndf, 4, 2, 1, bias=False)
          self.LReLU1 = nn.LeakyReLU(0.2, inplace=True)
          
          self.conv2 = nn.Conv2d(ndf, ndf * 2, 4, 1, 1, bias=False)
          self.BN1 = nn.BatchNorm2d(ndf * 2)
          self.LReLU2 = nn.LeakyReLU(0.2, inplace=True)
  
          self.conv3 = nn.Conv2d(ndf * 2, ndf * 4, 4, 1, 1, bias=False)
          self.BN2 = nn.BatchNorm2d(ndf * 4)
          self.LReLU3 = nn.LeakyReLU(0.2, inplace=True)
  
          self.conv4 = nn.Conv2d(ndf * 4, ndf * 8, 4, 1, 1, bias=False)
          self.BN3 = nn.BatchNorm2d(ndf * 8)
          self.LReLU4 = nn.LeakyReLU(0.2, inplace=True)
  
          self.conv5 = nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False)
          # print('hehe')
          # print(use_sigmoid)
          self.use_sigmoid = use_sigmoid
          if self.use_sigmoid:
              self.ac = nn.Sigmoid()
          # self.ReLU = nn.ReLU(True)
          # self.fc1 = nn.Linear(10752,4096)
          # self.fc2 = nn.Linear(4096,1000)
          #self.output = nn.Linear(21, 1)
  
      def spatial_pyramid_pool(self,previous_conv, num_sample, previous_conv_size, out_pool_size):
          '''
          previous_conv: a tensor vector of previous convolution layer
          num_sample: an int number of image in the batch
          previous_conv_size: an int vector [height, width] of the matrix features size of previous convolution layer
          out_pool_size: a int vector of expected output size of max pooling layer
      
          returns: a tensor vector with shape [1 x n] is the concentration of multi-level pooling
          '''    
          # print(previous_conv.size())
          for i in range(len(out_pool_size)):
              # print(previous_conv_size)
  
              h_wid = int(math.ceil(previous_conv_size[0] / out_pool_size[i]))
              w_wid = int(math.ceil(previous_conv_size[1] / out_pool_size[i]))
              h_pad = (h_wid*out_pool_size[i] - previous_conv_size[0] + 1)/2
              w_pad = (w_wid*out_pool_size[i] - previous_conv_size[1] + 1)/2
              maxpool = nn.MaxPool2d((h_wid, w_wid), stride=(h_wid, w_wid), padding=(h_pad, w_pad))
              x = maxpool(previous_conv)
              if(i == 0):
                  spp = x.view(num_sample,-1)
                  # print("spp size:",spp.size())
              else:
                  # print("size:",spp.size())
                  spp = torch.cat((spp,x.view(num_sample,-1)), 1)
          return spp
  
      def forward(self,x):
          x = self.conv1(x)
          x = self.LReLU1(x)
  
          x = self.conv2(x)
          #x = F.leaky_relu(self.BN1(x))
          x = self.LReLU2(self.BN1(x))
  
          x = self.conv3(x)
          #x = F.leaky_relu(self.BN2(x))
          x = self.LReLU3(self.BN2(x))
          
          x = self.conv4(x)
          # x = F.leaky_relu(self.BN3(x))
          x = self.LReLU4(self.BN3(x))
  
          x = self.conv5(x)
          # print(x.size())
          spp = self.spatial_pyramid_pool(x,1,[int(x.size(2)),int(x.size(3))],self.output_num)
          # print(spp.size())
          # fc1 = self.fc1(spp)
          # fc2 = self.fc2(fc1)
          # s = nn.Sigmoid()
          # output = s(fc2)
          #print(spp)
          #out = F.leaky_relu(spp)
          #out = F.relu(spp)
          #out = self.output(spp.view(x.size(0), -1))
          if self.use_sigmoid:
              out = self.ac(spp)
          else:
              out = spp
          #self.output = nn.Linear(30*30*1, 1)
  
          #return output.mean(0).view(-1)
          return out
  