# -*- coding: utf-8 -*-
"""
Created on Fri Feb 22 11:29:15 2019

@author: GKX
"""

#import keras

from deform_conv.deform_conv import tf_batch_map_offsets
from keras.layers import Conv3D
import tensorflow as tf
from keras.initializers import RandomNormal


class ConvOffset3D(Conv3D):
    """
    ConvOffset2D卷积层学习2D的偏移量，使用双线性插值输出变形后采样值
    """

    def __init__(self, filters, init_normal_stddev=0.01, **kwargs):
        """Init"""

        self.filters = filters
        super(ConvOffset3D, self).__init__(self.filters * 3, (3, 3, 3),
                                           padding='same',
                                           use_bias=False,
                                           # kernel_initializer='zeros',
                                           kernel_initializer=RandomNormal(0, init_normal_stddev),
                                           **kwargs)



    def call(self, x):
        """Return the deformed featured map"""
        # x = tf.transpose(x,[0,2,3,4,1])
        x_shape = x.get_shape()    #   (?, 64, 32, 32, 32)



        #   x_shape = (b,c,L,h,w)
        # 卷积输出得到2倍通道的feature map，获取到偏移量 大小为(b,3c,L,h,w)
        offsets = super(ConvOffset3D, self).call(x)   #    (?, 192, 32, 32, 32)




        # offsets: (b*c,l,h,w,3)
        offsets = self._to_bc_L_h_w_3(offsets, x_shape)   #   (?, 64, 32, 32, 3)



        # 将输入x也切换成这样: (b*c, L, h, w)
        x = self._to_bc_L_h_w(x, x_shape)    #   (?, 64, 32, 32)
        # print('>>>>>>>>>>>>>>>>>>>>>>>', x.shape)

        
        '''
        前面得到了偏移的(b∗c,h,w,2)和变形的输入(b∗c,h,w)。
        先将偏移原本的feature采样位置相加，得到实际的采样position
        调用tf_batch_map_coordinates做双线性插值操作，得到输出
        '''
        
        # 采样前形状：x:(b*c, L, h, w) ; 坐标为offsets（x,y）：(b*c, L, h, w, 3)
        # 双线性采样得到采样后的X_offset: (b*c, L, h, w)
        x_offset = tf_batch_map_offsets(x, offsets)
        # print('----------------------',x_offset.shape)

        # 再变原本的shape，即x_offset: b_c_L_h_w
        x_offset = self._to_b_c_L_h_w(x_offset, x_shape)   #   (?, 64, 32, 32, 32)
        # print('--------------------',x_offset.shape)

        return x_offset

    def compute_output_shape(self, input_shape):
        """Output shape is the same as input shape

        Because this layer does only the deformation part
        """
        return input_shape


#   x_shape = (b,c,L,h,w)
    @staticmethod
    def _to_bc_L_h_w_3(x, x_shape):
        """(b, h, w, 2c) -> (b*c, h, w, 2)  ##  (b, L, h, w, 2c) -> (b*c, L, h, w, 2)"""
        # x = tf.transpose(x, [0, 4, 1, 2, 3])
        x = tf.reshape(x, (-1, 
                           int(x_shape[2]),
                           int(x_shape[3]),
                           int(x_shape[4]), 3))
        return x

    @staticmethod
    def _to_bc_L_h_w(x, x_shape):
        """(b, h, w, c) -> (b*c, h, w)  ##  (b, L, h, w, c) -> (b*c, L, h, w)"""
        # x = tf.transpose(x, [0, 4, 1, 2, 3])
        x = tf.reshape(x, (-1, 
                           int(x_shape[2]),
                           int(x_shape[3]),
                           int(x_shape[4])))
        return x

    @staticmethod
    def _to_b_c_L_h_w(x, x_shape):
        """##  (b*c, L*h*w) -> (b, c, L, h, w)"""
        x = tf.reshape(x, (-1, 
                           int(x_shape[1]),
                           int(x_shape[2]),
                           int(x_shape[3]),
                           int(x_shape[4])))
        # x = tf.transpose(x, [0, 2, 3, 4, 1])
        return x

    
    

# -*- coding: utf-8 -*-
"""
Created on Fri Feb 22 11:29:15 2019

@author: GKX
"""

#import keras

from deform_conv.deform_conv import tf_batch_map_offsets
from keras.layers import Conv3D
import tensorflow as tf
from keras.initializers import RandomNormal


class ConvOffset3D(Conv3D):
    """
    ConvOffset2D卷积层学习2D的偏移量，使用双线性插值输出变形后采样值
    """

    def __init__(self, filters, init_normal_stddev=0.01, **kwargs):
        """Init"""

        self.filters = filters
        super(ConvOffset3D, self).__init__(self.filters * 3, (3, 3, 3),
                                           padding='same',
                                           use_bias=False,
                                           # kernel_initializer='zeros',
                                           kernel_initializer=RandomNormal(0, init_normal_stddev),
                                           **kwargs)



    def call(self, x):
        """Return the deformed featured map"""
        # x = tf.transpose(x,[0,2,3,4,1])
        x_shape = x.get_shape()    #   (?, 64, 32, 32, 32)



        #   x_shape = (b,c,L,h,w)
        # 卷积输出得到2倍通道的feature map，获取到偏移量 大小为(b,3c,L,h,w)
        offsets = super(ConvOffset3D, self).call(x)   #    (?, 192, 32, 32, 32)




        # offsets: (b*c,l,h,w,3)
        offsets = self._to_bc_L_h_w_3(offsets, x_shape)   #   (?, 64, 32, 32, 3)



        # 将输入x也切换成这样: (b*c, L, h, w)
        x = self._to_bc_L_h_w(x, x_shape)    #   (?, 64, 32, 32)
        # print('>>>>>>>>>>>>>>>>>>>>>>>', x.shape)

        
        '''
        前面得到了偏移的(b∗c,h,w,2)和变形的输入(b∗c,h,w)。
        先将偏移原本的feature采样位置相加，得到实际的采样position
        调用tf_batch_map_coordinates做双线性插值操作，得到输出
        '''
        
        # 采样前形状：x:(b*c, L, h, w) ; 坐标为offsets（x,y）：(b*c, L, h, w, 3)
        # 双线性采样得到采样后的X_offset: (b*c, L, h, w)
        x_offset = tf_batch_map_offsets(x, offsets)
        # print('----------------------',x_offset.shape)

        # 再变原本的shape，即x_offset: b_c_L_h_w
        x_offset = self._to_b_c_L_h_w(x_offset, x_shape)   #   (?, 64, 32, 32, 32)
        # print('--------------------',x_offset.shape)

        return x_offset

    def compute_output_shape(self, input_shape):
        """Output shape is the same as input shape

        Because this layer does only the deformation part
        """
        return input_shape


#   x_shape = (b,c,L,h,w)
    @staticmethod
    def _to_bc_L_h_w_3(x, x_shape):
        """(b, h, w, 2c) -> (b*c, h, w, 2)  ##  (b, L, h, w, 2c) -> (b*c, L, h, w, 2)"""
        # x = tf.transpose(x, [0, 4, 1, 2, 3])
        x = tf.reshape(x, (-1, 
                           int(x_shape[2]),
                           int(x_shape[3]),
                           int(x_shape[4]), 3))
        return x

    @staticmethod
    def _to_bc_L_h_w(x, x_shape):
        """(b, h, w, c) -> (b*c, h, w)  ##  (b, L, h, w, c) -> (b*c, L, h, w)"""
        # x = tf.transpose(x, [0, 4, 1, 2, 3])
        x = tf.reshape(x, (-1, 
                           int(x_shape[2]),
                           int(x_shape[3]),
                           int(x_shape[4])))
        return x

    @staticmethod
    def _to_b_c_L_h_w(x, x_shape):
        """##  (b*c, L*h*w) -> (b, c, L, h, w)"""
        x = tf.reshape(x, (-1, 
                           int(x_shape[1]),
                           int(x_shape[2]),
                           int(x_shape[3]),
                           int(x_shape[4])))
        # x = tf.transpose(x, [0, 2, 3, 4, 1])
        return x

    
    

# -*- coding: utf-8 -*- Created on Fri Feb 22 11:29:15 2019
 
 @author: GKX #import keras ConvOffset2D卷积层学习2D的偏移量，使用双线性插值输出变形后采样值 Init # kernel_initializer='zeros', Return the deformed featured map # x = tf.transpose(x,[0,2,3,4,1]) #   (?, 64, 32, 32, 32) #   x_shape = (b,c,L,h,w) # 卷积输出得到2倍通道的feature map，获取到偏移量 大小为(b,3c,L,h,w) #    (?, 192, 32, 32, 32) # offsets: (b*c,l,h,w,3) #   (?, 64, 32, 32, 3) # 将输入x也切换成这样: (b*c, L, h, w) #   (?, 64, 32, 32) # print('>>>>>>>>>>>>>>>>>>>>>>>', x.shape) 前面得到了偏移的(b∗c,h,w,2)和变形的输入(b∗c,h,w)。
         先将偏移原本的feature采样位置相加，得到实际的采样position
         调用tf_batch_map_coordinates做双线性插值操作，得到输出 # 采样前形状：x:(b*c, L, h, w) ; 坐标为offsets（x,y）：(b*c, L, h, w, 3) # 双线性采样得到采样后的X_offset: (b*c, L, h, w) # print('----------------------',x_offset.shape) # 再变原本的shape，即x_offset: b_c_L_h_w #   (?, 64, 32, 32, 32) # print('--------------------',x_offset.shape) Output shape is the same as input shape
 
         Because this layer does only the deformation part #   x_shape = (b,c,L,h,w) (b, h, w, 2c) -> (b*c, h, w, 2)  ##  (b, L, h, w, 2c) -> (b*c, L, h, w, 2) # x = tf.transpose(x, [0, 4, 1, 2, 3]) (b, h, w, c) -> (b*c, h, w)  ##  (b, L, h, w, c) -> (b*c, L, h, w) # x = tf.transpose(x, [0, 4, 1, 2, 3]) ##  (b*c, L*h*w) -> (b, c, L, h, w) # x = tf.transpose(x, [0, 2, 3, 4, 1])