//  [python]
def build_model(self):
        def filename2image(input_filenames, offset_height = None, offset_width = None, target_height=None, target_width=None):
            batch_size = len(input_filenames)
            if offset_height != None:
                offset_height = tf.split(offset_height, batch_size)
                offset_width = tf.split(offset_width, batch_size)

            images = []          
            for i in range(batch_size):
                file_contents = tf.read_file(input_filenames[i])
                image = tf.image.decode_png(file_contents, channels=3)
                if offset_height != None:
                    image = tf.image.crop_to_bounding_box(image, tf.reshape(offset_height[i], []), tf.reshape(offset_width[i], []), target_height, target_width)

                images.append(image)
            return tf.cast(tf.stack(images), tf.float32)

        #self.subject_num = SUBJECT_NUM_CASIA
            
        #self.id_labels_CASIA  = tf.placeholder(tf.float32, [self.batch_size, self.subject_num], name='id_labels_CASIA')
        self.m_300W_labels       = tf.placeholder(tf.float32, [self.batch_size, self.mDim], name='m_300W_labels')
        self.shape_300W_labels   = tf.placeholder(tf.float32, [self.batch_size, self.vertexNum * 3], name='shape_300W_labels')
        self.texture_300W_labels = tf.placeholder(tf.float32, [self.batch_size, self.texture_size[0], self.texture_size[1], self.c_dim], name='tex_300W_labels')
        #self.exp_300W_labels     = tf.placeholder(tf.float32, [self.batch_size, self.expDim], name='exp_300W_labels')
        #self.il_300W_labels      = tf.placeholder(tf.float32, [self.batch_size, self.ilDim], name='lighting_300W_labels')

        self.input_offset_height  = tf.placeholder(tf.int32, [self.batch_size], name='input_offset_height')
        self.input_offset_width   = tf.placeholder(tf.int32, [self.batch_size], name='input_offset_width')

        self.input_images_fn_300W = [tf.placeholder(dtype=tf.string) for _ in range(self.batch_size)]
        self.input_masks_fn_300W  = [tf.placeholder(dtype=tf.string) for _ in range(self.batch_size)]
        self.texture_labels_fn_300W = [tf.placeholder(dtype=tf.string) for _ in range(self.batch_size)]
        self.texture_masks_fn_300W  = [tf.placeholder(dtype=tf.string) for _ in range(self.batch_size)]


        
        #self.input_images_300W   = filename2image(self.input_images_fn_300W, offset_height = self.input_offset_height, offset_width = self.input_offset_width, target_height=self.output_size, target_width=self.output_size)
        #self.input_images_300W   = self.input_images_300W /127.5 - 1
        #self.input_masks_300W    = filename2image(self.input_masks_fn_300W,  offset_height = self.input_offset_height, offset_width = self.input_offset_width, target_height=self.output_size, target_width=self.output_size)
        #self.input_masks_300W    = self.input_masks_300W / 255.0
        

        #self.texture_300W_labels    = filename2image(self.texture_labels_fn_300W)
        #self.texture_300W_labels    = self.texture_300W_labels / 127.5 - 1

        #self.texture_mask_300W_labels = filename2image(self.texture_masks_fn_300W)
        #self.texture_mask_300W_labels = self.texture_mask_300W_labels / 255.0

        # For const alb loss
        self.albedo_indexes_x1 = tf.placeholder(tf.int32, [self.batch_size, CONST_PIXELS_NUM, 1], name='idexes_x1')
        self.albedo_indexes_y1 = tf.placeholder(tf.int32, [self.batch_size, CONST_PIXELS_NUM, 1], name='idexes_y1')

        self.albedo_indexes_x2 = tf.placeholder(tf.int32, [self.batch_size, CONST_PIXELS_NUM, 1], name='idexes_x2')
        self.albedo_indexes_y2 = tf.placeholder(tf.int32, [self.batch_size, CONST_PIXELS_NUM, 1], name='idexes_y2')

        self.const_alb_mask = load_const_alb_mask()

        

        def rendering(texture, m ,shape, background=None, extra_mask=None):
            image, mask = warp_texture(texture, m, shape, output_size=self.output_size, is_reduce = self.is_reduce)
            mask = tf.expand_dims(mask, -1)

            if extra_mask != None:
                mask = tf.multiply(extra_mask, mask)

            if background != None:
                image = tf.multiply(image, mask) + tf.multiply(background, 1 - mask)

            return image, mask

        def rendering_wflow(texture, flow_u, flow_v, flow_mask, background=None, extra_mask=None):
            image = bilinear_sampler(texture, flow_v, flow_u)
            mask = tf.expand_dims(flow_mask, -1)

            if extra_mask != None:
                mask = tf.multiply(extra_mask, mask)

            if background != None:
                image = tf.multiply(image, mask) + tf.multiply(background, 1 - mask)

            return image, mask

        def alb2tex(albedo, shade):
            return 2.0*tf.multiply( (albedo + 1.0)/2.0, shade) - 1


        def model_and_loss(input_images_fn_300W, input_masks_fn_300W, texture_labels_fn_300W, texture_masks_fn_300W, input_offset_height, input_offset_width, m_300W_labels, shape_300W_labels, albedo_indexes_x1, albedo_indexes_y1, albedo_indexes_x2, albedo_indexes_y2):
            batch_size = self.batch_size / self.gpu_num
            input_images_300W_   = filename2image(input_images_fn_300W, offset_height = input_offset_height, offset_width = input_offset_width, target_height=self.output_size, target_width=self.output_size)
            input_images_300W    = input_images_300W_ /127.5 - 1

            input_masks_300W    = filename2image(input_masks_fn_300W,  offset_height = input_offset_height, offset_width = input_offset_width, target_height=self.output_size, target_width=self.output_size)
            input_masks_300W    = input_masks_300W / 255.0

            texture_300W_labels    = filename2image(texture_labels_fn_300W)
            texture_300W_labels    = texture_300W_labels / 127.5 - 1

            texture_mask_300W_labels = filename2image(texture_masks_fn_300W)
            texture_mask_300W_labels = texture_mask_300W_labels / 255.0


            ## ------------------------- Network ---------------------------
            shape_fx_300W, alb_fx_300W, m_300W, il_300W  = self.generator_encoder( input_images_300W, is_reuse=False)
            
            if False:   #v2
                shape_base_300W, shape_2d_base_300W, shape_res_300W, shape_2d_res_300W = self.generator_decoder_shape(shape_fx_300W, is_reuse=False, is_training=True)
                shape_2d_300W = shape_2d_base_300W + shape_2d_res_300W
                shape_300W = shape_base_300W + shape_res_300W

                albedo_base_300W, albedo_res_300W  = self.generator_decoder_albedo(alb_fx_300W, is_reuse=False, is_training=True)
                albedo_300W = albedo_base_300W + albedo_res_300W
            else:       #v3

                shape_base_300W, shape_2d_base_300W, shape_300W, shape_2d_300W = self.generator_decoder_shape(shape_fx_300W, is_reuse=False, is_training=True)
                shape_2d_res_300W = shape_2d_300W - shape_2d_base_300W
                shape_res_300W = shape_300W - shape_base_300W

                albedo_base_300W, albedo_300W  = self.generator_decoder_albedo(alb_fx_300W, is_reuse=False, is_training=True)
                albedo_res_300W = albedo_300W - albedo_base_300W


            m_300W_full         = self.m2full(m_300W)
            m_300W_labels_full  = self.m2full(m_300W_labels)
            shape_base_300W_full    = self.shape2full(shape_base_300W)
            shape_300W_full         = self.shape2full(shape_300W)
            shape_300W_labels_full  = self.shape2full(shape_300W_labels)
            

            if self.is_gt_m: 
                m_4syn = m_300W_labels_full
            else:
                m_4syn = m_300W_full

            # Shade
            shade_base_300W, rotated_normal_2d  = generate_shade(il_300W, m_4syn, shape_base_300W_full, self.texture_size, is_reduce= self.is_reduce, is_with_normal=True)
            shade_300W       = generate_shade(il_300W, m_4syn, shape_300W_full,      self.texture_size, is_reduce= self.is_reduce)

            # Texture
            texture_base_300W     = alb2tex(albedo_base_300W, shade_base_300W)
            texture_mix_s0a1_300W = alb2tex(albedo_300W,      shade_base_300W)
            texture_mix_s1a0_300W = alb2tex(albedo_base_300W, shade_300W)
            texture_300W          = alb2tex(albedo_300W,      shade_300W)
            

            # Rendering
            G_flow_base_u, G_flow_base_v, G_flow_base_300W_mask = warping_flow(m_4syn, shape_base_300W_full, output_size=self.output_size, is_reduce = self.is_reduce)
            G_flow_u,      G_flow_v,      G_flow_300W_mask      = warping_flow(m_4syn, shape_300W_full,      output_size=self.output_size, is_reduce = self.is_reduce)

            G_images_base_300W, G_images_base_300W_mask = rendering_wflow(texture_base_300W,     G_flow_base_u, G_flow_base_v, G_flow_base_300W_mask, background=input_images_300W, extra_mask=input_masks_300W)
            G_images_mix_s0a1_300W, _                   = rendering_wflow(texture_mix_s0a1_300W, G_flow_base_u, G_flow_base_v, G_flow_base_300W_mask, background=input_images_300W, extra_mask=input_masks_300W)
            G_images_mix_s1a0_300W, _                   = rendering_wflow(texture_mix_s1a0_300W, G_flow_u,      G_flow_v,      G_flow_300W_mask,      background=input_images_300W, extra_mask=input_masks_300W)
            G_images_300W, G_images_300W_mask           = rendering_wflow(texture_300W,          G_flow_u,      G_flow_v,      G_flow_300W_mask,      background=input_images_300W, extra_mask=input_masks_300W)
            


            # Landmarks
            landmark_u_300W, landmark_v_300W                = compute_landmarks(m_4syn,             shape_300W_full,        output_size=self.output_size, is_reduce = self.is_reduce)
            landmark_u_base_300W, landmark_v_base_300W      = compute_landmarks(m_300W_full,        shape_base_300W_full,   output_size=self.output_size, is_reduce = self.is_reduce)
            landmark_u_base2_300W, landmark_v_base2_300W    = compute_landmarks(m_300W_full,        shape_300W_full,        output_size=self.output_size, is_reduce = self.is_reduce)
            landmark_u_300W_labels, landmark_v_300W_labels  = compute_landmarks(m_300W_labels_full, shape_300W_labels_full, output_size=self.output_size, is_reduce = self.is_reduce)

            

            ## ------------------------- Losses ---------------------------
            g_loss = tf.zeros(1)

            G_loss_shape   = 10*norm_loss(shape_base_300W, shape_300W_labels, loss_type = self.shape_loss)
            G_loss_m       = 5*norm_loss(m_300W,        m_300W_labels,     loss_type = 'l2')
            g_loss  += G_loss_m + G_loss_shape


            texture_vis_mask = tf.cast(tf.not_equal(texture_300W_labels, tf.ones_like(texture_300W_labels)*(-1)), tf.float32)
            texture_vis_mask = tf.multiply(texture_vis_mask, texture_mask_300W_labels)
            texture_ratio = tf.reduce_sum(texture_vis_mask)  / (batch_size* self.texture_size[0] * self.texture_size[1] * self.c_dim)

            

            uv_mask_tf = tf.expand_dims(tf.expand_dims(tf.constant( self.uv_mask, dtype = tf.float32 ), 0), -1)
            if self.is_batchwise_white_shading:
                

                mean_shade = tf.reduce_mean( tf.multiply(shade_base_300W, uv_mask_tf) , axis=[0,1,2]) * 16384 / 10379
                G_loss_white_shading = 10*norm_loss(mean_shade,  0.99*tf.ones([1, 3], dtype=tf.float32), loss_type = "l2")
            else:
                G_loss_white_shading = tf.zeros(1)

            g_loss += G_loss_white_shading

            
            G_loss_texture = (norm_loss(texture_base_300W,      texture_300W_labels, mask = texture_vis_mask, loss_type = self.tex_loss) +
                              #+norm_loss(texture_300W,           texture_300W_labels, mask = texture_vis_mask, loss_type = self.tex_loss) +
                              norm_loss(texture_mix_s0a1_300W,  texture_300W_labels, mask = texture_vis_mask, loss_type = self.tex_loss) +
                              norm_loss(texture_mix_s1a0_300W,  texture_300W_labels, mask = texture_vis_mask, loss_type = self.tex_loss)) / 2 / texture_ratio

            G_loss_recon_base     = 10*norm_loss(G_images_base_300W,     input_images_300W, loss_type = self.tex_loss ) / (tf.reduce_sum(G_images_base_300W_mask)/ (batch_size* self.output_size  * self.output_size))
            G_loss_recon_mix_s0a1 = 10*norm_loss(G_images_mix_s0a1_300W, input_images_300W, loss_type = self.tex_loss ) / (tf.reduce_sum(G_images_base_300W_mask)/ (batch_size* self.output_size  * self.output_size))
            G_loss_recon_mix_s1a0 = 10*norm_loss(G_images_mix_s1a0_300W, input_images_300W, loss_type = self.tex_loss ) / (tf.reduce_sum(G_images_300W_mask)     / (batch_size* self.output_size  * self.output_size))
            G_loss_recon          = 10*norm_loss(G_images_300W,          input_images_300W, loss_type = self.tex_loss ) / (tf.reduce_sum(G_images_300W_mask)     / (batch_size* self.output_size  * self.output_size))

            
            if self.is_smoothness:
                G_loss_smoothness = 5e5*norm_loss( (shape_2d_base_300W[:, :-2, 1:-1, :] + shape_2d_base_300W[:, 2:, 1:-1, :] + shape_2d_base_300W[:, 1:-1, :-2, :] + shape_2d_base_300W[:, 1:-1, 2:, :])/4.0,
                                                    shape_2d_base_300W[:, 1:-1, 1:-1, :], loss_type = 'l2') + \
                                        norm_loss( (shape_2d_300W[:, :-2, 1:-1, :] + shape_2d_300W[:, 2:, 1:-1, :] + shape_2d_300W[:, 1:-1, :-2, :] + shape_2d_300W[:, 1:-1, 2:, :])/4.0,
                                                    shape_2d_300W[:, 1:-1, 1:-1, :], loss_type = 'l2')
            else:
                G_loss_smoothness = tf.zeros(1)
            g_loss +=  G_loss_smoothness

            G_landmark_loss = (tf.reduce_mean(tf.nn.l2_loss(landmark_u_base_300W - landmark_u_300W_labels )) +  tf.reduce_mean(tf.nn.l2_loss(landmark_v_base_300W - landmark_v_300W_labels ))) / self.landmark_num / batch_size / 50 + \
                              (tf.reduce_mean(tf.nn.l2_loss(landmark_u_base2_300W - landmark_u_300W_labels )) +  tf.reduce_mean(tf.nn.l2_loss(landmark_v_base2_300W - landmark_v_300W_labels ))) / self.landmark_num / batch_size / 50 + \
                              (tf.reduce_mean(tf.nn.l2_loss(landmark_u_300W - landmark_u_300W_labels )) +  tf.reduce_mean(tf.nn.l2_loss(landmark_v_300W - landmark_v_300W_labels ))) / self.landmark_num / batch_size / 50


            #G_landmark_loss = ((tf.reduce_mean(tf.abs(landmark_u_base_300W - landmark_u_300W_labels )) +  tf.reduce_mean(tf.abs(landmark_v_base_300W - landmark_v_300W_labels ))) / self.landmark_num / batch_size  + \
            #                  (tf.reduce_mean(tf.abs(landmark_u_base2_300W - landmark_u_300W_labels )) +  tf.reduce_mean(tf.abs(landmark_v_base2_300W - landmark_v_300W_labels ))) / self.landmark_num / batch_size + \
            #                  (tf.reduce_mean(tf.abs(landmark_u_300W - landmark_u_300W_labels )) +  tf.reduce_mean(tf.abs(landmark_v_300W - landmark_v_300W_labels ))) / self.landmark_num / batch_size )* 1e2

            if self.is_using_symetry:
                albedo_base_300W_flip = tf.map_fn(lambda img: tf.image.flip_left_right(img), albedo_base_300W)
                G_loss_symetry = 10*norm_loss(tf.maximum(tf.abs(albedo_base_300W-albedo_base_300W_flip), 0.01), 0, loss_type = self.tex_loss)
            else:
                G_loss_symetry = tf.zeros(1)
            g_loss +=  G_loss_symetry

            if self.is_using_res_symetry:
                _, _, shape_res_300W_z = tf.split(shape_2d_res_300W, num_or_size_splits=3, axis=3)
                _, _, rotated_normal_z = tf.split(rotated_normal_2d, num_or_size_splits=3, axis=3)



                
                shape_res_300W_z_flip = tf.map_fn(lambda img: tf.image.flip_left_right(img), shape_res_300W_z)
                G_loss_res_symetry = 100*norm_loss(shape_res_300W_z, tf.stop_gradient(shape_res_300W_z_flip), mask= tf.cast( tf.less(rotated_normal_z, 0), tf.float32), loss_type = 'l1')
            else:
                G_loss_res_symetry = tf.zeros(1)
            g_loss +=  G_loss_res_symetry


            G_loss_res = norm_loss(shape_res_300W, 0, loss_type = 'l1') + norm_loss(albedo_res_300W, 0, loss_type = 'l1')
            g_loss += G_loss_res

            if self.is_const_albedo:

                albedo_1 = get_pixel_value(albedo_base_300W, albedo_indexes_x1, albedo_indexes_y1)
                albedo_2 = get_pixel_value(albedo_base_300W, albedo_indexes_x2, albedo_indexes_y2)

                G_loss_albedo_const = 10*norm_loss( tf.maximum(tf.abs(albedo_1- albedo_2), 0.01), 0, loss_type = self.tex_loss)
            else:
                G_loss_albedo_const = tf.zeros(1)
            g_loss += G_loss_albedo_const

            if self.is_const_local_albedo:
                local_albedo_alpha = 0.9
                texture_300W_labels_chromaticity = (texture_300W_labels + 1.0)/2.0
                texture_300W_labels_chromaticity = tf.divide(texture_300W_labels_chromaticity, tf.reduce_sum(texture_300W_labels_chromaticity, axis=[-1], keep_dims=True) + 1e-6)

                w_u = tf.stop_gradient(tf.exp(-15*tf.norm( texture_300W_labels_chromaticity[:, :-1, :, :] - texture_300W_labels_chromaticity[:, 1:, :, :], ord='euclidean', axis=-1, keep_dims=True)) * texture_vis_mask[:, :-1, :, :] )
                G_loss_local_albedo_const_u = tf.reduce_mean(norm_loss( albedo_base_300W[:, :-1, :, :], albedo_base_300W[:, 1:, :, :], loss_type = 'l2,1', reduce_mean=False, p=0.8) * w_u) / tf.reduce_sum(w_u+1e-6)

                    
                w_v = tf.stop_gradient(tf.exp(-15*tf.norm( texture_300W_labels_chromaticity[:, :, :-1, :] - texture_300W_labels_chromaticity[:, :, 1:, :], ord='euclidean', axis=-1, keep_dims=True)) * texture_vis_mask[:, :, :-1, :] )
                G_loss_local_albedo_const_v = tf.reduce_mean(norm_loss( albedo_base_300W[:, :, :-1, :], albedo_base_300W[:, :, 1:, :],  loss_type = 'l2,1', reduce_mean=False, p=0.8) * w_v) / tf.reduce_sum(w_v+1e-6)

                G_loss_local_albedo_const = (G_loss_local_albedo_const_u + G_loss_local_albedo_const_v)*10
            else:
                G_loss_local_albedo_const = tf.zeros(1)
            g_loss += G_loss_local_albedo_const

            if self.is_using_recon:
                g_loss +=  (G_loss_recon_base + 2*G_loss_recon_mix_s0a1 + 2*G_loss_recon_mix_s1a0)/2 #G_loss_recon +
            else:
                g_loss += G_loss_texture*4
            
            g_loss += G_loss_texture

            G_loss_frecon = tf.zeros(1)
            if self.is_using_frecon:
                from vgg_face import vgg_face


                #G_images_300W_vgg = (tf.multiply(G_images_300W, G_images_300W_mask)+ 1.0)*127.5
                G_images_mix_s1a0_300W_vgg = (G_images_mix_s1a0_300W + 1.0)*127.5
                G_images_mix_s0a1_300W_vgg = (G_images_mix_s0a1_300W + 1.0)*127.5
                input_images_300W_vgg      = (input_images_300W      + 1.0)*127.5

                input_features, _, _  = vgg_face('vgg-face.mat', input_images_300W_vgg)
                recon_mix_s1a0_features, _, _  = vgg_face('vgg-face.mat', G_images_mix_s1a0_300W_vgg)
                recon_mix_s0a1_features, _, _  = vgg_face('vgg-face.mat', G_images_mix_s0a1_300W_vgg)
                
                layer_names = ['conv1_1', 'conv2_1',  'conv3_1',  'conv4_1', 'conv5_1'] #['conv1_2', 'conv2_2',  'conv3_3',  'conv4_3', 'conv5_3']
                layer_weights = [50, 500, 2000, 3000, 250]

                for i in range(len(layer_names)):
                    layer_name = layer_names[i]
                    layer_weight = layer_weights[i]
                    G_loss_frecon += norm_loss(input_features[layer_name], recon_mix_s1a0_features[layer_name], loss_type='l2') / (layer_weight*layer_weight * len(layer_names))*2
                    G_loss_frecon += norm_loss(input_features[layer_name], recon_mix_s0a1_features[layer_name], loss_type='l2') / (layer_weight*layer_weight * len(layer_names))*2

            g_loss = g_loss + G_loss_frecon



            G_loss_graddiff = tf.zeros(1)
            if self.is_using_graddiff:
                G_images_300W_grad_u = G_images_300W[:, :-1, :, :] - G_images_300W[:, 1:, :, :]
                input_images_300W_grad_u = input_images_300W[:, :-1, :, :] - input_images_300W[:, 1:, :, :]


                G_images_300W_grad_v = G_images_300W[:, :, :-1, :] - G_images_300W[:, :, 1:, :]
                input_images_300W_grad_v = input_images_300W[:, :, :-1, :] - input_images_300W[:, :, 1:, :]

                G_loss_graddiff = 10* ( norm_loss(G_images_300W_grad_u, input_images_300W_grad_u, loss_type = 'l1' ) + 
                                        norm_loss(G_images_300W_grad_u, input_images_300W_grad_u, loss_type = 'l1' ) ) #/ (batch_size* self.output_size  * self.output_size)

            g_loss = g_loss + G_loss_graddiff

            G_loss_shade_mag = norm_loss(shade_base_300W, 1, mask= tf.cast(tf.greater(texture_base_300W , 1), dtype=tf.float32) , loss_type = 'l1')
            g_loss += G_loss_shade_mag






            '''
            # GAN loss
            if self.is_using_GAN:
                if not self.is_using_L2_GAN:

                    self.d_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=self.images_D_logit, labels= 0.