//  [python]
def analysis_network(image, num_output_components, blindspot, zero_last=False):

    def conv(n, name, n_out, size=3, gain=np.sqrt(2), zero_weights=False):
        if blindspot: assert (size % 2) == 1
        ofs = 0 if (not blindspot) else size // 2

        with tf.variable_scope(name):
            wshape = [size, size, n.shape[1].value, n_out]
            wstd = gain / np.sqrt(np.prod(wshape[:-1])) # He init.
            W = tf.get_variable('W', shape=wshape, initializer=(tf.initializers.zeros() if zero_weights else tf.initializers.random_normal(0., wstd)))
            b = tf.get_variable('b', shape=[n_out], initializer=tf.initializers.zeros())
            if ofs > 0: n = tf.pad(n, [[0, 0], [0, 0], [ofs, 0], [0, 0]])
            n = tf.nn.conv2d(n, W, strides=[1]*4, padding='SAME', data_format='NCHW') + tf.reshape(b, [1, -1, 1, 1])
            if ofs > 0: n = n[:, :, :-ofs, :]
        return n

    def up(n, name):
        with tf.name_scope(name):
            s = tf.shape(n)
            s = [-1, n.shape[1], s[2], s[3]]
            n = tf.reshape(n, [s[0], s[1], s[2], 1, s[3], 1])
            n = tf.tile(n, [1, 1, 1, 2, 1, 2])
            n = tf.reshape(n, [s[0], s[1], s[2] * 2, s[3] * 2])
        return n

    def down(n, name):
        with tf.name_scope(name):
            if blindspot: # Shift and pad if blindspot.
                n = tf.pad(n[:, :, :-1, :], [[0, 0], [0, 0], [1, 0], [0, 0]])
            n = tf.nn.max_pool(n, ksize=[1, 1, 2, 2], strides=[1, 1, 2, 2], padding='SAME', data_format='NCHW')
        return n

    def rotate(x, angle):
        if   angle == 0:   return x
        elif angle == 90:  return tf.transpose(x[:, :, :, ::-1], [0, 1, 3, 2])
        elif angle == 180: return x[:, :, ::-1, ::-1]
        elif angle == 270: return tf.transpose(x[:, :, ::-1, :], [0, 1, 3, 2])

    def concat(name, layers):
        return tf.concat(layers, axis=1, name=name)

    def LR(n, alpha=0.1):
        return tf.nn.leaky_relu(n, alpha=alpha, name='lrelu')

    # Input stage.

    if not blindspot:
        x = image
    else:
        x = tf.concat([rotate(image, a) for a in [0, 90, 180, 270]], axis=0)

    # Encoder part.

    pool0 = x
    x = LR(conv(x, 'enc_conv0', 48))
    x = LR(conv(x, 'enc_conv1', 48))
    x = down(x, 'pool1'); pool1 = x

    x = LR(conv(x, 'enc_conv2', 48))
    x = down(x, 'pool2'); pool2 = x

    x = LR(conv(x, 'enc_conv3', 48))
    x = down(x, 'pool3'); pool3 = x

    x = LR(conv(x, 'enc_conv4', 48))
    x = down(x, 'pool4'); pool4 = x

    x = LR(conv(x, 'enc_conv5', 48))
    x = down(x, 'pool5')

    x = LR(conv(x, 'enc_conv6', 48))

    # Decoder part.

    x = up(x, 'upsample5')
    x = concat('concat5', [x, pool4])
    x = LR(conv(x, 'dec_conv5a', 96))
    x = LR(conv(x, 'dec_conv5b', 96))

    x = up(x, 'upsample4')
    x = concat('concat4', [x, pool3])
    x = LR(conv(x, 'dec_conv4a', 96))
    x = LR(conv(x, 'dec_conv4b', 96))

    x = up(x, 'upsample3')
    x = concat('concat3', [x, pool2])
    x = LR(conv(x, 'dec_conv3a', 96))
    x = LR(conv(x, 'dec_conv3b', 96))

    x = up(x, 'upsample2')
    x = concat('concat2', [x, pool1])
    x = LR(conv(x, 'dec_conv2a', 96))
    x = LR(conv(x, 'dec_conv2b', 96))

    x = up(x, 'upsample1')
    x = concat('concat1', [x, pool0])

    # Output stages.

    if blindspot:
        # Blind-spot output stages.
        x = LR(conv(x, 'dec_conv1a', 96))
        x = LR(conv(x, 'dec_conv1b', 96))
        x = tf.pad(x[:, :, :-1, :], [[0, 0], [0, 0], [1, 0], [0, 0]])   # Shift and pad.
        x = tf.split(x, 4, axis=0)                                      # Split into rotations.
        x = [rotate(y, a) for y, a in zip(x, [0, 270, 180, 90])]        # Counterrotate.
        x = tf.concat(x, axis=1)                                        # Combine on channel axis.
        x = LR(conv(x, 'nin_a', 96*4, size=1))
        x = LR(conv(x, 'nin_b', 96, size=1))
        x = conv(x, 'nin_c', num_output_components, size=1, gain=1.0, zero_weights=zero_last)
    else:
        # Baseline network with postprocessing layers -- keep feature maps and distill with 1x1 convolutions.
        x = LR(conv(x, 'dec_conv1a', 96))
        x = LR(conv(x, 'dec_conv1b', 96))
        x = LR(conv(x, 'nin_a', 96, size=1))
        x = LR(conv(x, 'nin_b', 96, size=1))
        x = conv(x, 'nin_c', num_output_components, size=1, gain=1.0, zero_weights=zero_last)

    # Return results.

    return x