# Path: dltk/core/residual_unit.py
# def vanilla_residual_unit_3d(inputs,
#                              out_filters,
#                              kernel_size=(3, 3, 3),
#                              strides=(1, 1, 1),
#                              mode=tf.estimator.ModeKeys.EVAL,
#                              use_bias=False,
#                              activation=tf.nn.relu6,
#                              kernel_initializer=tf.initializers.variance_scaling(distribution='uniform'),
#                              bias_initializer=tf.zeros_initializer(),
#                              kernel_regularizer=None,
#                              bias_regularizer=None):
#     """Implementation of a 3D residual unit according to [1]. This
#         implementation supports strided convolutions and automatically
#         handles different input and output filters.
# 
#     [1] K. He et al. Identity Mappings in Deep Residual Networks. ECCV 2016.
# 
#     Args:
#         inputs (tf.Tensor): Input tensor to the residual unit. Is required to
#             have a rank of 5 (i.e. [batch, x, y, z, channels]).
#         out_filters (int): Number of convolutional filters used in
#             the sub units.
#         kernel_size (tuple, optional): Size of the convoltional kernels
#             used in the sub units
#         strides (tuple, optional): Convolution strides in (x,y,z) of sub
#             unit 0. Allows downsampling of the input tensor via strides
#             convolutions.
#         mode (str, optional): One of the tf.estimator.ModeKeys: TRAIN, EVAL or
#             PREDICT
#         activation (optional): A function to use as activation function.
#         use_bias (bool, optional): Train a bias with each convolution.
#         kernel_initializer (TYPE, optional): Initialisation of convolution kernels
#         bias_initializer (TYPE, optional): Initialisation of bias
#         kernel_regularizer (None, optional): Additional regularisation op
#         bias_regularizer (None, optional): Additional regularisation op
# 
#     Returns:
#         tf.Tensor: Output of the residual unit
#     """
# 
#     pool_op = tf.layers.max_pooling3d
# 
#     conv_params = {'padding': 'same',
#                    'use_bias': use_bias,
#                    'kernel_initializer': kernel_initializer,
#                    'bias_initializer': bias_initializer,
#                    'kernel_regularizer': kernel_regularizer,
#                    'bias_regularizer': bias_regularizer}
# 
#     in_filters = inputs.get_shape().as_list()[-1]
#     assert in_filters == inputs.get_shape().as_list()[-1], \
#         'Module was initialised for a different input shape'
# 
#     x = inputs
#     orig_x = x
# 
#     # Handle strided convolutions
#     if np.prod(strides) != 1:
#         orig_x = pool_op(inputs=orig_x,
#                          pool_size=strides,
#                          strides=strides,
#                          padding='valid')
# 
#     # Sub unit 0
#     with tf.variable_scope('sub_unit0'):
# 
#         # Adjust the strided conv kernel size to prevent losing information
#         k = [s * 2 if s > 1 else k for k, s in zip(kernel_size, strides)]
# 
#         x = tf.layers.batch_normalization(
#             x, training=mode == tf.estimator.ModeKeys.TRAIN)
#         x = activation(x)
# 
#         x = tf.layers.conv3d(
#             inputs=x,
#             filters=out_filters,
#             kernel_size=k, strides=strides,
#             **conv_params)
# 
#     # Sub unit 1
#     with tf.variable_scope('sub_unit1'):
#         x = tf.layers.batch_normalization(
#             x, training=mode == tf.estimator.ModeKeys.TRAIN)
#         x = activation(x)
# 
#         x = tf.layers.conv3d(
#             inputs=x,
#             filters=out_filters,
#             kernel_size=kernel_size,
#             strides=(1, 1, 1),
#             **conv_params)
# 
#     # Add the residual
#     with tf.variable_scope('sub_unit_add'):
# 
#         # Handle differences in input and output filter sizes
#         if in_filters < out_filters:
#             orig_x = tf.pad(
#                 tensor=orig_x,
#                 paddings=[[0, 0]] * (len(x.get_shape().as_list()) - 1) + [[
#                     int(np.floor((out_filters - in_filters) / 2.)),
#                     int(np.ceil((out_filters - in_filters) / 2.))]])
# 
#         elif in_filters > out_filters:
#             orig_x = tf.layers.conv3d(
#                 inputs=orig_x,
#                 filters=out_filters,
#                 kernel_size=kernel_size,
#                 strides=(1, 1, 1),
#                 **conv_params)
#         x += orig_x
# 
#     return x


import tensorflow as tf
from dltk.core.residual_unit import vanilla_residual_unit_3d

    x = inputs

    # Inital convolution with filters[0]
    k = [s * 2 if s > 1 else 3 for s in strides[0]]
    x = tf.layers.conv3d(x, filters[0], k, strides[0], **conv_params)
    tf.logging.info('Init conv tensor shape {}'.format(x.get_shape()))

    # Residual feature encoding blocks with num_res_units at different
    # resolution scales res_scales
    res_scales = [x]
    saved_strides = []
    for res_scale in range(1, len(filters)):

        # Features are downsampled via strided convolutions. These are defined
        # in `strides` and subsequently saved
        with tf.variable_scope('unit_{}_0'.format(res_scale)):

            x = vanilla_residual_unit_3d(
                inputs=x,
                out_filters=filters[res_scale],
                strides=strides[res_scale],
                activation=activation,
                mode=mode)
        saved_strides.append(strides[res_scale])

        for i in range(1, num_res_units):

            with tf.variable_scope('unit_{}_{}'.format(res_scale, i)):


# Path: dltk/core/residual_unit.py
# def vanilla_residual_unit_3d(inputs,
#                              out_filters,
#                              kernel_size=(3, 3, 3),
#                              strides=(1, 1, 1),
#                              mode=tf.estimator.ModeKeys.EVAL,
#                              use_bias=False,
#                              activation=tf.nn.relu6,
#                              kernel_initializer=tf.initializers.variance_scaling(distribution='uniform'),
#                              bias_initializer=tf.zeros_initializer(),
#                              kernel_regularizer=None,
#                              bias_regularizer=None):
#     """Implementation of a 3D residual unit according to [1]. This
#         implementation supports strided convolutions and automatically
#         handles different input and output filters.
# 
#     [1] K. He et al. Identity Mappings in Deep Residual Networks. ECCV 2016.
# 
#     Args:
#         inputs (tf.Tensor): Input tensor to the residual unit. Is required to
#             have a rank of 5 (i.e. [batch, x, y, z, channels]).
#         out_filters (int): Number of convolutional filters used in
#             the sub units.
#         kernel_size (tuple, optional): Size of the convoltional kernels
#             used in the sub units
#         strides (tuple, optional): Convolution strides in (x,y,z) of sub
#             unit 0. Allows downsampling of the input tensor via strides
#             convolutions.
#         mode (str, optional): One of the tf.estimator.ModeKeys: TRAIN, EVAL or
#             PREDICT
#         activation (optional): A function to use as activation function.
#         use_bias (bool, optional): Train a bias with each convolution.
#         kernel_initializer (TYPE, optional): Initialisation of convolution kernels
#         bias_initializer (TYPE, optional): Initialisation of bias
#         kernel_regularizer (None, optional): Additional regularisation op
#         bias_regularizer (None, optional): Additional regularisation op
# 
#     Returns:
#         tf.Tensor: Output of the residual unit
#     """
# 
#     pool_op = tf.layers.max_pooling3d
# 
#     conv_params = {'padding': 'same',
#                    'use_bias': use_bias,
#                    'kernel_initializer': kernel_initializer,
#                    'bias_initializer': bias_initializer,
#                    'kernel_regularizer': kernel_regularizer,
#                    'bias_regularizer': bias_regularizer}
# 
#     in_filters = inputs.get_shape().as_list()[-1]
#     assert in_filters == inputs.get_shape().as_list()[-1], \
#         'Module was initialised for a different input shape'
# 
#     x = inputs
#     orig_x = x
# 
#     # Handle strided convolutions
#     if np.prod(strides) != 1:
#         orig_x = pool_op(inputs=orig_x,
#                          pool_size=strides,
#                          strides=strides,
#                          padding='valid')
# 
#     # Sub unit 0
#     with tf.variable_scope('sub_unit0'):
# 
#         # Adjust the strided conv kernel size to prevent losing information
#         k = [s * 2 if s > 1 else k for k, s in zip(kernel_size, strides)]
# 
#         x = tf.layers.batch_normalization(
#             x, training=mode == tf.estimator.ModeKeys.TRAIN)
#         x = activation(x)
# 
#         x = tf.layers.conv3d(
#             inputs=x,
#             filters=out_filters,
#             kernel_size=k, strides=strides,
#             **conv_params)
# 
#     # Sub unit 1
#     with tf.variable_scope('sub_unit1'):
#         x = tf.layers.batch_normalization(
#             x, training=mode == tf.estimator.ModeKeys.TRAIN)
#         x = activation(x)
# 
#         x = tf.layers.conv3d(
#             inputs=x,
#             filters=out_filters,
#             kernel_size=kernel_size,
#             strides=(1, 1, 1),
#             **conv_params)
# 
#     # Add the residual
#     with tf.variable_scope('sub_unit_add'):
# 
#         # Handle differences in input and output filter sizes
#         if in_filters < out_filters:
#             orig_x = tf.pad(
#                 tensor=orig_x,
#                 paddings=[[0, 0]] * (len(x.get_shape().as_list()) - 1) + [[
#                     int(np.floor((out_filters - in_filters) / 2.)),
#                     int(np.ceil((out_filters - in_filters) / 2.))]])
# 
#         elif in_filters > out_filters:
#             orig_x = tf.layers.conv3d(
#                 inputs=orig_x,
#                 filters=out_filters,
#                 kernel_size=kernel_size,
#                 strides=(1, 1, 1),
#                 **conv_params)
#         x += orig_x
# 
#     return x


# Path: dltk/networks/regression_classification/resnet.py
import tensorflow as tf
from dltk.core.residual_unit import vanilla_residual_unit_3d


    x = inputs

    # Inital convolution with filters[0]
    k = [s * 2 if s > 1 else 3 for s in strides[0]]
    x = tf.layers.conv3d(x, filters[0], k, strides[0], **conv_params)
    tf.logging.info('Init conv tensor shape {}'.format(x.get_shape()))

    # Residual feature encoding blocks with num_res_units at different
    # resolution scales res_scales
    res_scales = [x]
    saved_strides = []
    for res_scale in range(1, len(filters)):

        # Features are downsampled via strided convolutions. These are defined
        # in `strides` and subsequently saved
        with tf.variable_scope('unit_{}_0'.format(res_scale)):

            x = vanilla_residual_unit_3d(
                inputs=x,
                out_filters=filters[res_scale],
                strides=strides[res_scale],
                activation=activation,
                mode=mode)
        saved_strides.append(strides[res_scale])

        for i in range(1, num_res_units):

            with tf.variable_scope('unit_{}_{}'.format(res_scale, i)):


                x = vanilla_residual_unit_3d(