예제 #1
0
def main():
    parser = argparse.ArgumentParser(
        description='Test a neural imaging pipeline')
    parser.add_argument('plot',
                        help='Plot type ({})'.format(
                            ', '.join(supported_plots)))
    parser.add_argument(
        '--data',
        dest='data',
        action='store',
        default='./data/rgb/clic256/',
        help='directory with training & validation images (png)')
    parser.add_argument('--images',
                        dest='images',
                        action='store',
                        default=10,
                        type=int,
                        help='number of images to test')
    parser.add_argument('--image',
                        dest='image_id',
                        action='store',
                        default=1,
                        type=int,
                        help='ID of the image to load')
    parser.add_argument('--patch',
                        dest='patch_size',
                        action='store',
                        default=128,
                        type=int,
                        help='training patch size')
    parser.add_argument('--dcn',
                        dest='dcn',
                        action='store',
                        help='directory with a trained DCN model')

    args = parser.parse_args()

    # Match the current
    args.plot = coreutils.match_option(args.plot, supported_plots)

    if args.plot == 'batch':
        model, stats = codec.restore_model(args.dcn,
                                           args.patch_size,
                                           fetch_stats=True)
        print('Training stats:', stats)

        data = dataset.IPDataset(args.data,
                                 load='y',
                                 n_images=0,
                                 v_images=args.images,
                                 val_rgb_patch_size=args.patch_size)
        batch_x = data.next_validation_batch(0, args.images)

        fig = show_example(model, batch_x)
        plt.show()
        plt.close()

    elif args.plot == 'jpeg-match-ssim':
        files, _ = loading.discover_files(args.data, n_images=-1, v_images=0)
        files = files[args.image_id:args.image_id + 1]
        batch_x = loading.load_images(files, args.data, load='y')
        batch_x = batch_x['y'].astype(np.float32) / (2**8 - 1)

        model = codec.restore_model(args.dcn, batch_x.shape[1])

        fig = match_jpeg(model, batch_x, match='ssim')
        plt.show()
        plt.close()

    elif args.plot == 'jpeg-match-bpp':
        files, _ = loading.discover_files(args.data, n_images=-1, v_images=0)
        files = files[args.image_id:args.image_id + 1]
        batch_x = loading.load_images(files, args.data, load='y')
        batch_x = batch_x['y'].astype(np.float32) / (2**8 - 1)

        model = codec.restore_model(args.dcn, batch_x.shape[1])

        fig = match_jpeg(model, batch_x, match='bpp')
        plt.show()
        plt.close()

    elif args.plot == 'jpg-trade-off':
        df = ratedistortion.get_jpeg_df(args.data, write_files=True)
        print(df.to_string())

    elif args.plot == 'jp2-trade-off':
        df = ratedistortion.get_jpeg2k_df(args.data, write_files=True)
        print(df.to_string())

    elif args.plot == 'dcn-trade-off':
        df = ratedistortion.get_dcn_df(args.data, args.dcn, write_files=False)
        print(df.to_string())

    elif args.plot == 'bpg-trade-off':
        df = ratedistortion.get_bpg_df(args.data, write_files=False)
        print(df.to_string())

    else:
        print('Error: Unknown plot!')
예제 #2
0
def main():

    parser = argparse.ArgumentParser(
        description='Train a neural imaging pipeline')

    # Parameters related to the training data
    parser.add_argument(
        '--data',
        dest='data',
        action='store',
        default='./data/rgb/32k',
        help='directory with training & validation images (png)')
    parser.add_argument(
        '--split',
        dest='split',
        action='store',
        default='16000:800:2',
        help=
        'data split with #training:#validation:#validation_patches - e.g., 16000:800:2'
    )
    parser.add_argument('--patch',
                        dest='patch_size',
                        action='store',
                        default=128,
                        type=int,
                        help='training patch size')

    # Parameters of the DCN
    parser.add_argument('--dcn',
                        dest='dcn',
                        action='store',
                        default='TwitterDCN',
                        help='specific DCN class name')
    parser.add_argument(
        '--params',
        dest='dcn_params',
        action='append',
        help='Extra parameters for DCN constructor (JSON string)')
    parser.add_argument('--param_list',
                        dest='dcn_param_list',
                        default=None,
                        help='CSV file with DCN configurations')

    # General
    parser.add_argument('--out',
                        dest='out_dir',
                        action='store',
                        default='./data/models/dcn/playground',
                        help='output directory for storing trained models')
    parser.add_argument('--epochs',
                        dest='epochs',
                        action='store',
                        default=1500,
                        type=int,
                        help='maximum number of training epochs')
    parser.add_argument(
        '--v_schedule',
        dest='validation_schedule',
        action='store',
        default=100,
        type=int,
        help='Validation schedule - evaluate the model every v_schedule epochs'
    )
    parser.add_argument('--lr',
                        dest='learning_rate',
                        action='store',
                        default=1e-4,
                        type=float,
                        help='learning rate')
    parser.add_argument('--v_train',
                        dest='validation_is_training',
                        action='store_true',
                        default=False,
                        help='Use the model in training mode while testing')
    parser.add_argument(
        '--no_aug',
        dest='no_aug',
        action='store_true',
        default=False,
        help='disable data augmentation (flipping + gamma correction)')
    parser.add_argument(
        '--resume',
        dest='resume',
        action='store_true',
        default=False,
        help='Resume training from last checkpoint, if possible')
    parser.add_argument('--dry',
                        dest='dry',
                        action='store_true',
                        default=False,
                        help='Dry run (no training - only does model setup)')
    parser.add_argument(
        '--group',
        dest='run_group',
        action='store',
        type=int,
        default=None,
        help='Specify run group (sub-selects scenarios for running)')
    parser.add_argument(
        '--fill',
        dest='fill',
        action='store',
        default=None,
        help='Path of the extended scenarios table with appended result columns'
    )

    args = parser.parse_args()

    if not args.dcn:
        print('A DCN needs to be specified!')
        parser.print_usage()
        sys.exit(1)

    parameters = pd.DataFrame(
        columns=['scenario', 'label', 'active', 'run_group'])

    try:
        if args.dcn_params is not None:

            for p in args.dcn_params:
                cli_params = json.loads(p.replace('\'', '"'))
                cli_params['scenario'] = np.nan
                cli_params['label'] = 'command-line'
                cli_params['active'] = True
                cli_params['run_group'] = np.nan

                parameters = parameters.append(cli_params, ignore_index=True)

        if args.dcn_param_list is not None:
            parameters = parameters.append(pd.read_csv(args.dcn_param_list),
                                           ignore_index=True,
                                           sort=True)

    except json.decoder.JSONDecodeError as e:
        print('WARNING', 'JSON parsing error: ', e)
        sys.exit(2)

    # Round the number of epochs to align with the sampling rate
    args.epochs = int(
        np.ceil(args.epochs / args.validation_schedule) *
        args.validation_schedule) + 1

    training_spec = {
        'seed': 1234,
        'dataset': args.data,
        'n_images': int(args.split.split(':')[0]),
        'v_images': int(args.split.split(':')[1]),
        'valid_patches': int(args.split.split(':')[2]),
        'n_epochs': args.epochs,
        'batch_size': 50,
        'patch_size': args.patch_size,
        'sample_dropout': False,
        'learning_rate': args.learning_rate,
        'learning_rate_reduction_schedule': 1000,
        'learning_rate_reduction_factor': 0.5,
        'validation_schedule': args.validation_schedule,
        'convergence_threshold': 1e-5,
        'current_epoch': 0,
        'validation_is_training': args.validation_is_training,
        'augmentation_probs': {
            'resize': 0.0,
            'flip_h': 0.0 if args.no_aug else 0.5,
            'flip_v': 0.0 if args.no_aug else 0.5,
            'gamma': 0.0 if args.no_aug else 0.5,
        }
    }

    if np.sum(parameters['active'] == True) == 0:
        parameters.append({
            'name': 'default',
            'active': True
        },
                          ignore_index=True)

    print('DCN model: {}'.format(args.dcn))

    if args.run_group is not None:
        parameters = parameters[parameters['run_group'] == args.run_group]

    parameters = parameters[parameters['active']].drop(
        columns=['active', 'run_group'])
    print('# DCN parameter list [{} active configs]:\n'.format(
        len(parameters)))
    print(parameters)

    print('\n# Training Spec:')
    for key, value in training_spec.items():
        print(' {:50s}: {}'.format(key, value))

    # Load the dataset
    if not args.dry:
        print('\n# Dataset:')
        np.random.seed(training_spec['seed'])
        data = dataset.IPDataset(
            args.data,
            n_images=training_spec['n_images'],
            v_images=training_spec['v_images'],
            load='y',
            val_rgb_patch_size=training_spec['patch_size'],
            val_n_patches=training_spec['valid_patches'])

        for key in ['Training', 'Validation']:
            print('{:>16s} [{:5.1f} GB] : Y -> {} '.format(
                '{} data'.format(key), coreutils.mem(data[key.lower()]['y']),
                data[key.lower()]['y'].shape),
                  flush=True)

    model_log = {}

    # If requested, add columns to include results
    parameters['ssim'] = np.nan
    parameters['entropy'] = np.nan
    parameters['loss'] = np.nan

    print('\n# Training:\n')

    for counter, (index, params) in enumerate(
            parameters.drop(columns=['scenario', 'label']).iterrows()):

        print('## Scenario {} - {} / {}'.format(index, counter + 1,
                                                len(parameters)))
        # Create TF session and graph
        graph = tf.Graph()
        sess = tf.Session(graph=graph)

        # Create a DCN according to the spec
        dcn_params = {
            k: v
            for k, v in params.to_dict().items() if not utils.is_nan(v)
        }
        dcn_params['default_val_is_train'] = training_spec[
            'validation_is_training']
        dcn = getattr(compression,
                      args.dcn)(sess,
                                graph,
                                None,
                                patch_size=training_spec['patch_size'],
                                **dcn_params)

        model_code = dcn.model_code

        if model_code in model_log:
            print(
                'WARNING - model {} already registered by scenario {}'.format(
                    model_code, index))
            model_log[model_code].append(index)
        else:
            model_log[model_code] = [index]

        if not args.dry:
            train_dcn({'dcn': dcn}, training_spec, data, args.out_dir)

        # Fill the table with results, if requested
        if args.fill is not None:

            results_json = os.path.join(args.out_dir, dcn.model_code,
                                        dcn.scoped_name, 'progress.json')

            if os.path.isfile(results_json):

                with open(results_json) as f:
                    results = json.load(f)

                parameters.loc[
                    index,
                    'ssim'] = results['performance']['ssim']['validation'][-1]
                parameters.loc[
                    index,
                    'loss'] = results['performance']['loss']['validation'][-1]
                parameters.loc[index, 'entropy'] = results['performance'][
                    'entropy']['training'][-1]

        # Cleanup
        sess.close()
        del graph

    if args.fill is not None:
        if args.fill == '-':
            print(parameters.to_string())
        elif args.fill.endswith('.csv'):
            print('Saving the results to {}'.format(args.fill))
            parameters.to_csv(args.fill, index=False)
        else:
            raise ValueError(
                'Invalid value for the output results file: {}'.format(
                    args.fill))

    if args.dry:
        print('List of instantiated models [{}]:'.format(len(model_log)))
        for index, key in enumerate(sorted(model_log.keys())):
            print('{}  {:3d}. {} -> {}'.format(
                ' ' if len(model_log[key]) == 1 else '!', index, key,
                model_log[key]))
예제 #3
0
def batch_training(nip_model,
                   camera_names=None,
                   root_directory=None,
                   loss_metric='L2',
                   jpeg_quality=50,
                   jpeg_mode='sin',
                   use_pretrained=True,
                   end_repetition=10,
                   start_repetition=0,
                   n_epochs=1001,
                   nip_directory=None,
                   split='120:30:4',
                   regularization_strengths=None):
    """
    Repeat training for multiple NIP regularization strengths.
    """

    training = {
        'use_pretrained_nip': use_pretrained,
        'n_epochs': n_epochs,
        'patch_size': 128,
        'batch_size': 20,
        'sampling_rate': 50,
        'learning_rate': 1e-4,
        'n_images': int(split.split(':')[0]),
        'v_images': int(split.split(':')[1]),
        'val_n_patches': int(split.split(':')[2])
    }

    if root_directory is None:
        raise FileNotFoundError(
            'Invalid root directory: {}'.format(root_directory))

    if not os.path.isdir(root_directory):
        os.makedirs(root_directory)

    if nip_directory is None or not os.path.isdir(nip_directory):
        raise FileNotFoundError(
            'Invalid NIP snapshots directory: {}'.format(nip_directory))

    # Experiment setup
    camera_names = camera_names or [
        'Nikon D90', 'Nikon D7000', 'Canon EOS 5D', 'Canon EOS 40D'
    ]
    if regularization_strengths is None or len(regularization_strengths) == 0:
        regularization_strengths = [
            0, 1e-4, 5e-4, 1e-3, 5e-3, 1e-2, 5e-2, 0.1, 0.25, 0.5, 1
        ]
    else:
        regularization_strengths = [float(x) for x in regularization_strengths]

    # Construct the TF model
    tf_ops, distribution = construct_models(nip_model,
                                            distribution_jpeg=jpeg_quality,
                                            loss_metric=loss_metric,
                                            jpeg_approx=jpeg_mode)

    for camera_name in camera_names:

        print('\n# Loading data for camera {}'.format(camera_name))

        training['camera_name'] = camera_name

        # Load the dataset for the given camera
        data_directory = os.path.join('./data/raw/nip_training_data/',
                                      camera_name)

        # Find available images
        data = dataset.IPDataset(data_directory,
                                 n_images=training['n_images'],
                                 v_images=training['v_images'],
                                 load='xy',
                                 val_rgb_patch_size=2 * training['patch_size'],
                                 val_n_patches=training['val_n_patches'])

        # Repeat evaluation
        for rep in range(start_repetition, end_repetition):
            for reg in regularization_strengths:
                training['nip_weight'] = reg
                training['run_number'] = rep
                train_manipulation_nip(tf_ops, training, distribution, data, {
                    'root': root_directory,
                    'nip_snapshots': nip_directory
                })
예제 #4
0
def main():
    parser = argparse.ArgumentParser(description='Test manipulation detection (FAN) on RGB images')
    group = parser.add_argument_group('General settings')
    group.add_argument('--patch', dest='patch', action='store', default=64, type=int,
                        help='patch size')
    group.add_argument('--patches', dest='patches', action='store', default=1, type=int,
                        help='number of validation patches')
    group.add_argument('--data', dest='data', action='store', default='./data/rgb/32k',
                        help='directory with test RGB images')

    group = parser.add_argument_group('Training session selection')
    group.add_argument('--dir', dest='dir', action='store', default='./data/m/7-raw',
                        help='directory with training sessions')
    group.add_argument('--re', dest='re', action='store', default=None,
                        help='regular expression to filter training sessions')

    group = parser.add_argument_group('Override training settings')
    group.add_argument('--jpeg', dest='jpeg', action='store', default=None, type=int,
                        help='Override JPEG quality level (distribution channel)')
    group.add_argument('--dcn', dest='dcn_model', action='store', default=None,
                        help='DCN compression model path')
    group.add_argument('--manip', dest='manipulations', action='store', default=None,
                       help='Included manipulations, e.g., : {}'.format('sharpen,jpeg,resample,gaussian'))

    args = parser.parse_args()

    # Split manipulations
    if args.manipulations is not None:
        args.manipulations = args.manipulations.strip().split(',')

    json_files = sorted(str(f) for f in Path(args.dir).glob('**/training.json'))

    if len(json_files) == 0:
        sys.exit(0)

    # Load training / validation data
    data = dataset.IPDataset(args.data, n_images=0, v_images=-1, load='y', val_rgb_patch_size=2 * args.patch, val_n_patches=args.patches)

    print('Found {} candidate training sessions ({})'.format(len(json_files), args.dir))
    print('Data: {}'.format(data.description))

    for filename in json_files:
        if args.re is None or re.findall(args.re, filename):

            with open(filename) as f:
                training_log = json.load(f)

            # Setup manipulations
            if args.manipulations is None:
                manipulations = eval(coreutils.getkey(training_log, 'summary/Classes'))
                # TODO More elegant solution needed
                manipulations.remove('native')
                if 'jpg' in manipulations:
                    manipulations.append('jpeg')
                    manipulations.remove('jpg')
            else:
                manipulations = args.manipulations

            accuracy = coreutils.getkey(training_log, 'forensics/validation/accuracy')[-1]
            compression = coreutils.getkey(training_log, 'summary/Channel Compression')
            downsampling = coreutils.getkey(training_log, 'summary/Channel Downsampling')

            # Setup compression
            if compression.startswith('jpeg'):
                # Override from CLI arguments or read from training log
                if args.jpeg is not None:
                    jpeg = args.jpeg
                else:
                    jpeg = int(re.findall('\(([0-9]+),', compression)[0])
                dcn_model = None
            else:
                jpeg = None

                if args.dcn_model is not None:
                    # Override from CLI arguments
                    dcn_model = args.dcn_model
                else:
                    # Otherwise, read from the training log
                    if 'dcn' in coreutils.getkey(training_log, 'summary/Joint optimization'):
                        # If the DCN is trainable, load the fine-tuned model
                        dcn_model = os.path.join(os.path.split(filename)[0], 'models')
                    else:
                        # If not trainable, load a baseline DCN model
                        compression_params = eval(coreutils.getkey(training_log, 'summary/Channel Compression Parameters'))
                        dcn_model = compression_params['dirname']

            print('\n> {}'.format(filename))
            print('Compression: {}'.format(compression))
            print('Downsampling: {}'.format(downsampling))

            conf_mat, labels = validate_fan(os.path.join(os.path.split(filename)[0], 'models'), manipulations, data, args.patch, dcn_model, downsampling, jpeg)

            print(results_data.confusion_to_text((100*conf_mat).round(0), labels, filename, 'txt'))
            print('Accuracy: {:.2f} // Expected {:.2f}'.format(np.mean(np.diag(conf_mat)), accuracy))
            print(';{};{:.4f};{:.4f}'.format(os.path.split(filename)[0], np.mean(np.diag(conf_mat)), accuracy))
예제 #5
0
def main():
    parser = argparse.ArgumentParser(
        description='Train a neural imaging pipeline')
    parser.add_argument('--cam', dest='camera', action='store', help='camera')
    parser.add_argument('--nip',
                        dest='nips',
                        action='append',
                        choices=["INet", "UNet", "DNet", "OctUNet", "UNet3D"],
                        help='add NIP for training (repeat if needed)')
    parser.add_argument('--out',
                        dest='out_dir',
                        action='store',
                        default='./checkpoint/nip_model_snapshots',
                        help='output directory for storing trained NIP models')
    parser.add_argument(
        '--data',
        dest='data_dir',
        action='store',
        default='../../datasets/raw/nip_training_data/',
        help='input directory with training data (.npy and .png pairs)')
    parser.add_argument('--patch',
                        dest='patch_size',
                        action='store',
                        default=512,
                        type=int,
                        help='training patch size (RGB)')
    parser.add_argument('--epochs',
                        dest='epochs',
                        action='store',
                        default=25000,
                        type=int,
                        help='maximum number of training epochs')
    parser.add_argument('--batch',
                        dest='batch_size',
                        action='store',
                        default=20,
                        type=int,
                        help='training batch size')
    parser.add_argument(
        '--params',
        dest='nip_params',
        default=None,
        help='Extra parameters for NIP constructor (JSON string)')
    parser.add_argument(
        '--resume',
        dest='resume',
        action='store_true',
        default=False,
        help='Resume training from last checkpoint, if possible')
    parser.add_argument(
        '--split',
        dest='split',
        action='store',
        default='270:30:1',
        help=
        'data split with #training:#validation:#validation_patches - e.g., 120:30:1'
    )
    parser.add_argument('--ext',
                        dest='extension',
                        action='store',
                        default='png',
                        help='file extension of rgb images - e.g., png, JPG')

    args = parser.parse_args()

    if not args.camera:
        print('A camera needs to be specified!')
        parser.print_usage()
        sys.exit(1)

    if not args.nips:
        print('At least one NIP needs to be specified!')
        parser.print_usage()
        sys.exit(1)

    data_directory = os.path.join(args.data_dir, args.camera)
    out_directory_root = args.out_dir

    try:
        if args.nip_params is not None:
            args.nip_params = json.loads(args.nip_params.replace('\'', '"'))
    except json.decoder.JSONDecodeError:
        print('WARNING', 'JSON parsing error for: ',
              args.nip_params.replace('\'', '"'))
        sys.exit(2)

    print('## Parameters summary')
    print('Camera : {}'.format(args.camera))
    print('NIPs   : {}'.format(args.nips))
    print('Params : {}'.format(args.nip_params))
    print('Input  : {}'.format(data_directory))
    print('Output : {}'.format(out_directory_root))
    print('Resume : {}'.format(args.resume))

    # Load training and validation data
    training_spec = {
        'seed': 1234,
        'n_images': int(args.split.split(':')[0]),
        'v_images': int(args.split.split(':')[1]),
        'valid_patches': int(args.split.split(':')[2]),
        'valid_patch_size': 512,
    }

    np.random.seed(training_spec['seed'])

    # Load and summarize the training data
    data = dataset.IPDataset(
        data_directory,
        n_images=training_spec['n_images'],
        v_images=training_spec['v_images'],
        load='xy',
        val_rgb_patch_size=training_spec['valid_patch_size'],
        val_n_patches=training_spec['valid_patches'],
        rgb_extension=args.extension)

    for key in ['Training', 'Validation']:
        print('{:>16s} [{:5.1f} GB] : X -> {}, Y -> {} '.format(
            '{} data'.format(key),
            coreutils.mem(data[key.lower()]['x']) +
            coreutils.mem(data[key.lower()]['y']),
            data[key.lower()]['x'].shape, data[key.lower()]['y'].shape),
              flush=True)

    # Lazy loading to prevent delays in basic CLI interaction
    from models import pipelines
    import tensorflow as tf

    # Train the Desired NIP Models
    for pipe in args.nips:

        if not issubclass(getattr(pipelines, pipe), pipelines.NIPModel):
            supported_nips = [
                x for x in dir(pipelines)
                if x != 'NIPModel' and type(getattr(pipelines, x)) is type
                and issubclass(getattr(pipelines, x), pipelines.NIPModel)
            ]
            raise ValueError(
                'Invalid NIP model ({})! Available NIPs: ({})'.format(
                    pipe, supported_nips))

        args.nip_params = args.nip_params or {}

        tf.reset_default_graph()
        gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=1)
        config = tf.ConfigProto(allow_soft_placement=True,
                                gpu_options=gpu_options)
        config.gpu_options.allow_growth = True
        sess = tf.Session(config=config)
        model = getattr(pipelines, pipe)(sess,
                                         tf.get_default_graph(),
                                         loss_metric='L1',
                                         **args.nip_params)
        model.sess.run(tf.global_variables_initializer())

        train_nip_model(model,
                        args.camera,
                        args.epochs,
                        validation_loss_threshold=1e-5,
                        patch_size=args.patch_size,
                        resume=args.resume,
                        sampling_rate=1000,
                        batch_size=args.batch_size,
                        learning_rate=1e-4,
                        data=data,
                        out_directory_root=args.out_dir)

        sess.close()

    return
예제 #6
0
def batch_training(nip_model,
                   camera_names=None,
                   root_directory=None,
                   loss_metric='L2',
                   trainables=None,
                   jpeg_quality=None,
                   jpeg_mode='soft',
                   manipulations=None,
                   dcn_model=None,
                   downsampling='pool',
                   end_repetition=10,
                   start_repetition=0,
                   n_epochs=1001,
                   patch=128,
                   use_pretrained=True,
                   lambdas_nip=None,
                   lambdas_dcn=None,
                   nip_directory=None,
                   split='120:30:4'):
    """
    Repeat training for multiple NIP regularization strengths.
    """

    if nip_model is None:
        raise FileNotFoundError('NIP model not specified!')

    if nip_directory is None or not os.path.isdir(nip_directory):
        raise FileNotFoundError(
            'Invalid NIP snapshots directory: {}'.format(nip_directory))

    if root_directory is None:
        raise FileNotFoundError(
            'Invalid root directory: {}'.format(root_directory))

    if not os.path.isdir(root_directory):
        os.makedirs(root_directory)

    # Lazy loading to minimize delays when checking cli parameters
    from training.manipulation import construct_models, train_manipulation_nip

    camera_names = camera_names or [
        'Nikon D90', 'Nikon D7000', 'Canon EOS 5D', 'Canon EOS 40D'
    ]

    training = {
        'use_pretrained_nip': use_pretrained,
        'n_epochs': n_epochs,
        'patch_size': patch,
        'batch_size': 20,
        'sampling_rate': 50,
        'learning_rate': 1e-4,
        'n_images': int(split.split(':')[0]),
        'v_images': int(split.split(':')[1]),
        'val_n_patches': int(split.split(':')[2]),
    }

    # Setup trainable elements and regularization ----------------------------------------------------------------------

    trainables = trainables if trainables is not None else set()
    for tr in trainables:
        if tr not in {'nip', 'dcn'}:
            raise ValueError(
                'Invalid specifier of trainable elements: only nip, dcn allowed!'
            )

    training['trainable'] = trainables

    if lambdas_nip is None or len(lambdas_nip) == 0:
        lambdas_nip = [1e-4, 5e-4, 1e-3, 5e-3, 1e-2, 5e-2, 0.1, 0.25, 0.5, 1
                       ] if 'nip' in trainables else [0]

    else:
        lambdas_nip = [float(x) for x in lambdas_nip]

    if lambdas_dcn is None or len(lambdas_dcn) == 0:
        lambdas_dcn = [0.1, 0.05, 0.01, 0.005, 0.001
                       ] if 'dcn' in trainables else [0]
    else:
        lambdas_dcn = [float(x) for x in lambdas_dcn]

    if downsampling not in ['pool', 'bilinear', 'none']:
        raise ValueError('Unsupported channel down-sampling')

    if dcn_model is None and jpeg_quality is None:
        jpeg_quality = 50

    # Define the distribution channel ----------------------------------------------------------------------------------
    compression_params = {}
    if jpeg_quality is not None:
        compression_params['quality'] = jpeg_quality
        compression_params['rounding_approximation'] = jpeg_mode
    else:
        if dcn_model in codec.dcn_presets:
            dcn_model = codec.dcn_presets[dcn_model]
        compression_params['dirname'] = dcn_model

    if jpeg_quality is not None:
        compression = 'jpeg'
    elif dcn_model is not None:
        compression = 'dcn'
    else:
        compression = 'none'

    # Parse manipulations
    manipulations = manipulations or [
        'sharpen', 'resample', 'gaussian', 'jpeg'
    ]

    distribution_spec = {
        'downsampling': downsampling,
        'compression': compression,
        'compression_params': compression_params
    }

    # Construct the TF model
    tf_ops, distribution = construct_models(nip_model,
                                            patch_size=training['patch_size'],
                                            trainable=trainables,
                                            distribution=distribution_spec,
                                            manipulations=manipulations,
                                            loss_metric=loss_metric)

    for camera_name in camera_names:

        print('\n# Loading data for {}'.format(camera_name))

        training['camera_name'] = camera_name

        # Load the dataset
        if nip_model == 'ONet':
            # TODO Dirty hack - if the NIP model is the dummy empty model, load RGB images only
            data_directory = os.path.join(root_directory, 'rgb', camera_name)
            patch_mul = 2
            load = 'y'
        else:
            # Otherwise, load (RAW, RGB) pairs for a specific camera
            data_directory = os.path.join(root_directory, 'raw',
                                          'training_data', camera_name)
            patch_mul = 2
            load = 'xy'

        # If the target root directory has no training images, fallback to use the default root
        if not os.path.isdir(data_directory):
            print(
                'WARNING Training images not found in the target root directory - using default root as image source'
            )
            data_directory = data_directory.replace(root_directory, 'data/')
            data_directory = data_directory.replace('//', '/')

        # Find available images
        data = dataset.IPDataset(data_directory,
                                 n_images=training['n_images'],
                                 v_images=training['v_images'],
                                 load=load,
                                 val_rgb_patch_size=patch_mul *
                                 training['patch_size'],
                                 val_n_patches=training['val_n_patches'])

        # data = dataset.IPDataset(data_directory, n_images=training['n_images'], v_images=training['v_images'], load=load, val_rgb_patch_size=training['patch_size'], val_n_patches=training['val_n_patches'])

        # Repeat evaluation
        for rep in range(start_repetition, end_repetition):
            for lr in lambdas_nip:
                for lc in lambdas_dcn:
                    training['lambda_nip'] = lr
                    training['lambda_dcn'] = lc
                    training['run_number'] = rep
                    train_manipulation_nip(tf_ops, training, distribution,
                                           data, {
                                               'root': root_directory,
                                               'nip_snapshots': nip_directory
                                           })