"""
This is file performs the convolutional neural network algorithm, in which the k fold is performed as well.
The results were saved in a csv file.
"""

import os
import sys
from datetime import datetime

import numpy as np
from astropy.io import fits
from astropy.utils.data import get_pkg_data_filename
from matplotlib import pyplot as plt
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split, StratifiedKFold
from sklearn.utils import shuffle
from tensorflow.python.keras import Sequential
from tensorflow.python.keras.callbacks import ModelCheckpoint, EarlyStopping
from tensorflow.python.keras.layers.convolutional import Conv2D, MaxPooling2D
from tensorflow.python.keras.layers.core import Dense, Dropout, Flatten
from tensorflow.python.keras.models import Model
from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from tensorflow.python.keras.utils.vis_utils import plot_model
from ExcelUtils import createExcelSheet, writeToFile
import tensorflow
print(tensorflow.__version__)

now = datetime.now()
dt_string = now.strftime("%d_%m_%Y_%H_%M_%S")
print(dt_string)
excel_headers = []
excel_dictionary = []
excel_headers.append("Date and Time")
excel_dictionary.append(dt_string)

# Globals
makeNewCSVFile = True
max_num = sys.maxsize  # Set to sys.maxsize when running entire data set
max_num_testing = sys.maxsize  # Set to sys.maxsize when running entire data set
max_num_prediction = sys.maxsize  # Set to sys.maxsize when running entire data set
validation_split = 0.2  # A float value between 0 and 1 that determines what percentage of the training
# data is used for validation.
k_fold_num = 5  # A number between 1 and 10 that determines how many times the k-fold classifier
# is trained.
epochs = 5  # A number that dictates how many iterations should be run to train the classifier
batch_size = 128  # The number of items batched together during training.
run_k_fold_validation = True  # Set this to True if you want to run K-Fold validation as well.
input_shape = (100, 100, 3)  # The shape of the images being learned & evaluated.
augmented_multiple = 2  # This uses data augmentation to generate x-many times as much data as there is on file.
use_augmented_data = True  # Determines whether to use data augmentation or not.
patience_num = 10  # Used in the early stopping to determine how quick/slow to react.
use_early_stopping = True  # Determines whether to use early stopping or not.
use_model_checkpoint = True  # Determines whether the classifiers keeps track of the most accurate iteration of itself.
monitor_early_stopping = 'val_loss'
monitor_model_checkpoint = 'val_acc'
use_shuffle = True

# Adding global parameters to excel
excel_headers.append("Max Training Num")
excel_dictionary.append(max_num)
excel_headers.append("Max Testing Num")
excel_dictionary.append(max_num_testing)
excel_headers.append("Max Prediction Num")
excel_dictionary.append(max_num_prediction)
excel_headers.append("Validation Split")
excel_dictionary.append(validation_split)
excel_headers.append("K fold Num")
excel_dictionary.append(k_fold_num)
excel_headers.append("Epochs")
excel_dictionary.append(epochs)
excel_headers.append("Batch Size")
excel_dictionary.append(batch_size)
excel_headers.append("Run K fold")
excel_dictionary.append(run_k_fold_validation)
excel_headers.append("Input Shape")
excel_dictionary.append(input_shape)
excel_headers.append("Augmented Multiple")
excel_dictionary.append(augmented_multiple)
excel_headers.append("Use Augmented Data")
excel_dictionary.append(use_augmented_data)
excel_headers.append("Patience")
excel_dictionary.append(patience_num)
excel_headers.append("Use Early Stopping")
excel_dictionary.append(use_early_stopping)
excel_headers.append("Use Model Checkpoint")
excel_dictionary.append(use_model_checkpoint)
excel_headers.append("Monitor Early Stopping")
excel_dictionary.append(monitor_early_stopping)
excel_headers.append("Monitor Model Checkpoint")
excel_dictionary.append(monitor_model_checkpoint)

if not os.path.exists('../Results/g_r_%s/' % dt_string):
    os.mkdir('../Results/g_r_%s/' % dt_string)


# Helper methods
def getPositiveImages(images_dir, max_num, input_shape):
    """
    This gets the positively simulated images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the positively simulated images.
        max_num(integer):   This is the number of sources of the positively simulated images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        positive_images(numpy array):   This is the numpy array of the positively simulated images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        positive_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/%s_g_norm.fits' % (os.path.join(root, folder), folder))
            r_img_path = get_pkg_data_filename('%s/%s_r_norm.fits' % (os.path.join(root, folder), folder))
            i_img_path = get_pkg_data_filename('%s/%s_i_norm.fits' % (os.path.join(root, folder), folder))

            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]

            img_data = [g_data, r_data, i_data]
            positive_images[index] = img_data
            index += 1

            if index >= num_of_images:
                break
        return positive_images.reshape(num_of_images, input_shape[0], input_shape[1], input_shape[2])


def getNegativeImages(images_dir, max_num, input_shape):
    """
    This gets the negative images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the negative images.
        max_num(integer):   This is the number of sources of the negative images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        negative_images(numpy array):   This is the numpy array of the negative images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        negative_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % (os.path.join(root, folder)))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % (os.path.join(root, folder)))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % (os.path.join(root, folder)))

            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]

            img_data = [g_data, r_data, i_data]
            negative_images[index] = img_data
            index += 1

            if index >= num_of_images:
                break
        return negative_images.reshape(num_of_images, input_shape[0], input_shape[1], input_shape[2])


def getUnseenData(images_dir, max_num, input_shape):
    """
        This gets the unseen images in the g, r and  i bands containing the identified known lenses.
        Args:
            images_dir(string): This is the file path address of the unseen images.
            max_num(integer):   This is the number of sources of the unseen images to be used.
            input_shape(tuple): This is the shape of the images.
        Returns:
            des_tiles(dictionary):   This is the dictionary of the unseen images with the shape of
                                            (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                            (num_of_images, 100, 100, 3).
        """

    des_tiles = {}

    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % (os.path.join(root, folder)))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % (os.path.join(root, folder)))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % (os.path.join(root, folder)))

            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]

            img_data = np.array([g_data, r_data, i_data]).reshape(input_shape[0], input_shape[1], input_shape[2])
            des_tiles.update({folder: img_data})
            index += 1
            if index >= num_of_images:
                break

        return des_tiles


def makeImageSet(positive_images, negative_images=None, known_des_names=None, neg_des_names=None,
                 shuffle_needed=use_shuffle):
    """
    This is used to create data set of images and labels, in which the positive and negative images are all
    combined and shuffled.
    Args:
        positive_images(numpy array):   This is the numpy array of the positively simulated images.
        negative_images(numpy array):   This is the numpy array of the negative images, this is set to a
                                        default of None.
        known_des_names(list):    This is the dictionary of the unseen known lenses, this is set to a
                                        default of None.
        neg_des_names(list):      This is the dictionary of the negative images, this is set to a
                                        default of None.
        shuffle_needed(boolean):        This is a boolean value to determine whether or not shuffling of the given data
                                        sets is required.
    Returns:
        image_set(numpy array):         This is the image data set of  numpy array of the combination positive
                                        and negative images.
        label_set(numpy array):         This is the label data set of  numpy array of the combination positive
                                        and negative label.
        des_names_set(numpy array):     This is the des name data set of the known lenses and negative images used.
    """
    # if negative_images is None:
    #     negative_images = []
    #     # known_des_names = {}
    #     neg_des_names = {}

    image_set = []
    label_set = []
    des_names_set = []

    # If there is none in objects for the known_des_names and neg_des_names
    if known_des_names is None and neg_des_names is None:
        for index_none in range(0, len(positive_images)):
            image_set.append(positive_images[index_none])
            label_set.append(1)

        for index_none in range(0, len(negative_images)):
            image_set.append(negative_images[index_none])
            label_set.append(0)

        if shuffle_needed:
            image_set, label_set = shuffle(image_set, label_set)

    else:  # if there is names for des
        for index_des in range(0, len(positive_images)):
            image_set.append(positive_images[index_des])
            label_set.append(1)
            des_names_set.append(known_des_names[index_des])

        for index_des in range(0, len(negative_images)):
            image_set.append(negative_images[index_des])
            label_set.append(0)
            des_names_set.append(neg_des_names[index_des])

        if shuffle_needed:
            image_set, label_set, des_names_set = shuffle(image_set, label_set, des_names_set)

    return np.array(image_set), np.array(label_set), np.array(des_names_set)


def buildClassifier(input_shape=(100, 100, 3)):
    """
    This creates the CNN algorithm.
    Args:
        input_shape(tuple): This is the image shape of (100,100,3)
    Returns:
        classifier(sequential): This is the sequential model.
    """
    # Initialising the CNN
    classifier = Sequential()
    classifier.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape, padding='same'))
    classifier.add(MaxPooling2D(pool_size=(3, 3), padding='same'))
    classifier.add(Dropout(0.5))  # added extra Dropout layer
    classifier.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Conv2D(128, (3, 3), padding='same', activation='relu'))
    classifier.add(Dropout(0.5))  # added extra dropout layer
    classifier.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dropout(0.2))  # antes era 0.25
    classifier.add(Conv2D(512, (3, 3), padding='same', activation='relu'))
    classifier.add(Conv2D(1024, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dense(units=1024, activation='relu'))  # added new dense layer
    classifier.add(Dropout(0.2))  # antes era 0.25
    # Step 3 - Flattening
    classifier.add(Flatten())
    classifier.add(Dense(units=1024, activation='relu'))  # added new dense layer
    classifier.add(Dense(units=256, activation='relu'))  # added new dense layer
    # Step 4 - Full connection
    classifier.add(Dropout(0.2))
    classifier.add(Dense(units=1, activation='sigmoid'))
    classifier.summary()

    # Compiling the CNN
    classifier.compile(optimizer='adam',
                       loss='binary_crossentropy',
                       metrics=['accuracy'])
    plot_model(classifier, to_file='model_plot.png', show_shapes=True, show_layer_names=True)
    return classifier


def visualiseActivations(img_tensor, base_dir):
    """
    This makes images of the activations, as the selected image passed through the model
    Args:
        img_tensor(numpy array):    This is the numpy array of the selected image
        base_dir(string):           This is the file path name
    Saves:
        This saves the activation images of the selected source.
    """
    global predicted_class, size
    # Run prediction on that image
    predicted_class = classifier.predict_classes(img_tensor, batch_size=10)
    print("Predicted class is: ", predicted_class)
    # Visualize activations
    layer_outputs = [layer.output for layer in classifier.layers[:12]]
    activation_model = Model(inputs=classifier.input, outputs=layer_outputs)
    activations = activation_model.predict(img_tensor)
    layer_names = []
    for layer in classifier.layers[:12]:
        layer_names.append(layer.name)
    images_per_row = 3
    count = 0
    for layer_name, layer_activation in zip(layer_names, activations):
        number_of_features = layer_activation.shape[-1]
        size = layer_activation.shape[1]
        number_of_columns = number_of_features // images_per_row
        display_grid = np.zeros((size * number_of_columns, images_per_row * size))
        for col in range(number_of_columns):
            for row in range(images_per_row):
                channel_image = layer_activation[0, :, :, col * images_per_row + row]
                channel_image -= channel_image.mean()
                channel_image /= channel_image.std()
                channel_image *= 64
                channel_image += 128
                channel_image = np.clip(channel_image, 0, 255).astype('uint8')
                display_grid[col * size: (col + 1) * size, row * size: (row + 1) * size] = channel_image
        scale = 1. / size
        activations_figure = plt.figure(figsize=(scale * display_grid.shape[1],
                                                 scale * display_grid.shape[0]))
        plt.title(layer_name)
        plt.grid(False)
        plt.imshow(display_grid, aspect='auto', cmap='viridis')
        activations_figure.savefig('%s/%s_Activation_%s.png' % (base_dir, count, layer_name))
        plt.close()

        count += 1


def usingCnnModel(training_data, training_labels, val_data, val_labels):
    """
    This is using the CNN model and setting it up.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
        val_data(numpy arrays):         This is the numpy array of the validation data.
        val_labels(numpy arrays):       This is the numpy array of the validation labels.
    Returns:
        history(history):               This is the history of the classifier.
        classifier(sequential):         This is the cnn model classifier fitted to the training data and labels.
    """
    model_checkpoint = ModelCheckpoint(filepath="best_weights.hdf5",
                                       monitor=monitor_model_checkpoint,
                                       save_best_only=True)

    early_stopping = EarlyStopping(monitor=monitor_early_stopping, patience=patience_num)  # original patience =3

    classifier = buildClassifier()
    callbacks_array = []
    if use_early_stopping:
        callbacks_array.append(early_stopping)
    if use_model_checkpoint:
        callbacks_array.append(model_checkpoint)

    print(len(training_data))
    history = classifier.fit(training_data,
                             training_labels,
                             epochs=epochs,
                             validation_data=(val_data, val_labels),
                             callbacks=callbacks_array,
                             batch_size=batch_size
                             # steps_per_epoch=int(len(training_data) / batch_size),
                             )
    return history, classifier


def createAugmentedData(training_data, training_labels):
    """
    This is creates the augmented data.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
    Returns:
        complete_training_data_set(numpy array):    This is the numpy array of the total training data, which is has
                                                    undergone augmentation.
        complete_training_labels_set(numpy array):  This is the numpy array of the total training labels, which is has
                                                    undergone augmentation.
    """
    complete_training_data_set = []
    complete_training_labels_set = []

    for data in training_data:
        complete_training_data_set.append(data)
    print("Complete Training Data: " + str(len(complete_training_data_set)))

    for label in training_labels:
        complete_training_labels_set.append(label)
    print("Complete Training Label: " + str(len(complete_training_labels_set)))

    # create augmented data
    data_augmented = ImageDataGenerator(featurewise_center=True,
                                        featurewise_std_normalization=True,
                                        rotation_range=90,
                                        width_shift_range=0.2,
                                        height_shift_range=0.2,
                                        horizontal_flip=True,
                                        vertical_flip=True)
    data_augmented.fit(training_data)

    training_data_size = training_data.shape[0]
    aug_counter = 0
    while aug_counter < (augmented_multiple - 1):
        iterator = data_augmented.flow(training_data, training_labels, batch_size=training_data_size)
        # iterator = data_augmented.flow(training_data, training_labels, batch_size=batch_size)
        augmented_data = iterator.next()
        for data in augmented_data[0]:
            complete_training_data_set.append(data)
        for label in augmented_data[1]:
            complete_training_labels_set.append(label)
        aug_counter += 1

    print("Size of All Training Data: " + str(len(complete_training_data_set)))
    print("Size of All Training Labels: " + str(len(complete_training_labels_set)))

    array_training_data = np.array(complete_training_data_set)
    array_training_labels = np.array(complete_training_labels_set)

    print("Shape of complete training data: " + str(array_training_data.shape))
    print("Shape of complete training labels: " + str(array_training_labels.shape))

    return np.array(complete_training_data_set), np.array(complete_training_labels_set)


def savePredictedLenses(des_names_array, predicted_class_probabilities, predicted_lenses_filepath, text_file_path):
    """
    This saves the names of the predicted lenses in the respective textfiles.
    Args:
        des_names_array(numpy array): This is a list of the des names of the sources.
        predicted_class_probabilities(list):    This is a list of the probabilities in which lenses are predicted by
                                                the algorithm.
        predicted_lenses_filepath(string):      This is the string of the predicted lenses filepath, where this needs
                                                to be saved in the directory.
        text_file_path(string):                 This is the text file path address to which these images are saved.
    Saves:
        text_file(.txt file):                   This is the text file saved containing the predicted lenses DES names.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)
    text_file = open('%s' % text_file_path, "a+")
    text_file.write('Predicted Lenses: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 1:
            text_file.write("%s \n " % des_names_array[lens_index])

    text_file.write('\n')
    text_file.write('\n')

    text_file.write('No Lenses Predicted: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 0:
            text_file.write("%s \n " % des_names_array[lens_index])
    text_file.close()


def gettingTrueFalsePositiveNegatives(testing_data, testing_labels, text_file_path,
                                      predicted_lenses_filepath, kf_counter=0):
    """
    This is used to get the True/False Positive and Negative values gained from the CNN confusion matrix.
    Args:
        testing_data(numpy array):          This is the unseen testing data numpy array.
        testing_labels(numpy array):        This is the unseen testing label numpy array.
        text_file_path(string):             This is the file path name of the text file in which the confusion
                                            matrix is saved.
        predicted_lenses_filepath(string):  This is the file path in which the text file is saved.
    Saves:
        This saves a confusion matrix of the True/False Positive and Negative values.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)

    predicted_data = classifier.predict_classes(testing_data)
    true_negative, false_positive, false_negative, true_positive = confusion_matrix(testing_labels,
                                                                                    predicted_data.round()).ravel()
    matrix = (confusion_matrix(testing_labels, predicted_data.round()))
    print(str(matrix) + ' \n ')
    print("True Positive: %s \n" % true_positive)
    print("False Negative: %s \n" % false_negative)
    print("False Positive: %s \n" % false_positive)
    print("True Negative: %s \n" % true_negative)

    text_file = open('%s' % text_file_path, "a+")
    text_file.write('KFold Number: %s \n' % str(kf_counter))
    text_file.write('Predicted vs True Matrix: \n')
    text_file.write(str(matrix) + " \n ")
    text_file.write("True Negative: %s \n" % str(true_negative))
    text_file.write("False Positive: %s \n" % str(false_positive))
    text_file.write("False Negative: %s \n" % str(false_negative))
    text_file.write("True Positive: %s \n" % str(true_positive))
    text_file.write("\n")
    text_file.close()

    confusion_matrix_array = [true_negative, false_positive, false_negative, true_positive]
    return confusion_matrix_array


def gettingKFoldConfusionMatrix(test_data, test_labels, images_47, labels_47, images_84, labels_84, all_unseen_images,
                                all_unseen_labels, kf_counter):
    test_confusion_matrix = gettingTrueFalsePositiveNegatives(test_data,
                                                              test_labels,
                                                              text_file_path='../Results/%s/TrainingTestingResults'
                                                                             '/KFold_PredictedMatrix.txt' % dt_string,
                                                              predicted_lenses_filepath='../Results/%s/TrainingTestingResults'
                                                                                        % dt_string,
                                                              kf_counter=kf_counter)

    confusion_matrix_47 = gettingTrueFalsePositiveNegatives(images_47,
                                                            labels_47,
                                                            text_file_path='../Results/%s/Predicted47'
                                                                           '/KFold_47_LensesPredicted.txt' %
                                                                           dt_string,
                                                            predicted_lenses_filepath='../Results/%s/Predicted47'
                                                                                      % dt_string,
                                                            kf_counter=kf_counter)

    confusion_matrix_84 = gettingTrueFalsePositiveNegatives(images_84,
                                                            labels_84,
                                                            text_file_path='../Results/%s/Predicted84'
                                                                           '/KFold_84_LensesPredicted.txt' %
                                                                           dt_string,
                                                            predicted_lenses_filepath='../Results/%s/Predicted84'
                                                                                      % dt_string,
                                                            kf_counter=kf_counter)

    all_confusion_matrix = gettingTrueFalsePositiveNegatives(all_unseen_images,
                                                             all_unseen_labels,
                                                             text_file_path='../Results/%s/All_Predicted'
                                                                            '/KFold_All_LensesPredicted.txt' % dt_string,
                                                             predicted_lenses_filepath='../Results/%s'
                                                                                       '/All_Predicted' %
                                                                                       dt_string,
                                                             kf_counter = kf_counter)

    return test_confusion_matrix, confusion_matrix_47, confusion_matrix_84, all_confusion_matrix


def executeKFoldValidation(train_data, train_labels, val_data, val_labels, test_data, test_labels,
                           images_47, labels_47, images_84, labels_84, all_unseen_images, all_unseen_labels):
    """
    This does the k fold cross validation which is tested against the unseen testing and known lenses.
    Args:
        train_data(numpy arrays):           This is the numpy array of the training data.
        train_labels(numpy arrays):         This is the numpy array of the training labels.
        val_data(numpy arrays):             This is the numpy array of the validation data.
        val_labels(numpy arrays):           This is the numpy array of the validation labels.
        testing_data(numpy array):          This is the numpy array of the unseen testing data.
        testing_labels(numpy array):        This is the numpy array of the unseen testing label.
        images_47(numpy array):             This is the numpy array of the unseen DES images data.
        labels_47(numpy array):             This is the numpy array of the unseen DES images labels.
        images_84(numpy array):             This is the numpy array of the unseen Jacobs images data.
        labels_84(numpy array):             This is the numpy array of the unseen Jacobs images labels.
        all_unseen_images(numpy array):     This is the numpy array of the unseen DES + Jacobs images data.
        all_unseen_labels(numpy array):     This is the numpy array of the unseen DES + Jacobs images labels.

    Saves:
        This saves the scores, mean and std. of the unseen data that is evaluated in the k fold cross validation.
    """
    if run_k_fold_validation:
        print("In executingKFoldValidation")

        # this is doing it manually:
        kfold = StratifiedKFold(n_splits=k_fold_num, shuffle=True)

        test_scores_list = []
        unseen_47_scores_list = []
        unseen_84_scores_list = []
        all_unseen_scores_list = []

        test_matrix_list = []
        matrix_47_list = []
        matrix_84_list = []
        all_matrix_list = []
        kf_counter = 0

        for train, test in kfold.split(train_data, train_labels):
            kf_counter += 1
            print('KFold #:', kf_counter)

            model = buildClassifier()
            # fit the model
            model.fit(train_data[train],
                      train_labels[train],
                      epochs=epochs,
                      validation_data=(val_data, val_labels),
                      batch_size=batch_size
                      )

            unseen_47_scores = model.evaluate(images_47, labels_47, batch_size=batch_size)
            unseen_47_scores_list.append(unseen_47_scores[1] * 100)
            unseen_84_scores = model.evaluate(images_84, labels_84, batch_size=batch_size)
            unseen_84_scores_list.append(unseen_84_scores[1] * 100)
            test_scores = model.evaluate(test_data, test_labels, batch_size=batch_size)
            test_scores_list.append(test_scores[1] * 100)
            all_unseen_score = model.evaluate(all_unseen_images, all_unseen_labels, batch_size=batch_size)
            all_unseen_scores_list.append(all_unseen_score[1] * 100)

            # show confusion matrix
            test_confusion_matrix, confusion_matrix_47, confusion_matrix_84, all_confusion_matrix = \
                gettingKFoldConfusionMatrix(test_data, test_labels, images_47, labels_47, images_84, labels_84,
                                            all_unseen_images,
                                            all_unseen_labels, kf_counter)
            test_matrix_list.append(test_confusion_matrix)
            matrix_47_list.append(confusion_matrix_47)
            matrix_84_list.append(confusion_matrix_84)
            all_matrix_list.append(all_confusion_matrix)

        test_scores_mean = np.mean(test_scores_list)
        test_scores_std = np.std(test_scores_list)
        unseen_47_mean = np.mean(unseen_47_scores_list)
        unseen_47_std = np.std(unseen_47_scores_list)
        unseen_84_mean = np.mean(unseen_84_scores_list)
        unseen_84_std = np.std(unseen_84_scores_list)
        all_unseen_mean = np.mean(all_unseen_scores_list)
        all_unseen_std = np.std(all_unseen_scores_list)

        print("Test Scores: " + str(test_scores_list))
        print("Test Scores Mean: " + str(test_scores_mean))
        print("Test Scores Std: " + str(test_scores_std))
        print("Unseen 47 Scores: " + str(unseen_47_scores_list))
        print("Unseen 47 Scores Mean: " + str(unseen_47_mean))
        print("Unseen 47 Scores Std: " + str(unseen_47_std))
        print("Unseen 84 Scores: " + str(unseen_84_scores_list))
        print("Unseen 84 Scores Mean: " + str(unseen_84_mean))
        print("Unseen 84 Scores Std: " + str(unseen_84_std))
        print("All Unseen Scores: " + str(all_unseen_scores_list))
        print("All Unseen Scores Mean: " + str(all_unseen_mean))
        print("All Unseen Scores Std: " + str(all_unseen_std))
        print("Test Confusion Matrices: " + str(test_matrix_list))
        print("47 Confusion Matrices: " + str(matrix_47_list))
        print("84 Confusion Matrices: " + str(matrix_84_list))
        print("All Confusion Matrices: " + str(all_matrix_list))

        excel_headers.append("Test Scores Mean")
        excel_dictionary.append(test_scores_mean)
        excel_headers.append("Test Scores Std")
        excel_dictionary.append(test_scores_std)
        excel_headers.append("Unseen 47 Scores Mean")
        excel_dictionary.append(unseen_47_mean)
        excel_headers.append("Unseen 47 Scores Std")
        excel_dictionary.append(unseen_47_std)
        excel_headers.append("Unseen 84 Scores Mean")
        excel_dictionary.append(unseen_84_mean)
        excel_headers.append("Unseen 84 Scores Std")
        excel_dictionary.append(unseen_84_std)
        excel_headers.append("All Unseen Scores Mean")
        excel_dictionary.append(all_unseen_mean)
        excel_headers.append("All Unseen Scores Std")
        excel_dictionary.append(all_unseen_std)

        plt.plot(test_scores_list, color='red', label='Testing Scores')
        plt.plot(unseen_47_scores_list, color='blue', label='Unseen 47 Scores')
        plt.plot(unseen_84_scores_list, color='black', label='Unseen 84 Scores')
        plt.plot(all_unseen_scores_list, color='green', label='Unseen Scores')
        plt.xlabel('Folds')
        plt.ylabel('Accuracy')
        plt.legend()

        plt.show()


# __________________________________________________________________________
# MAIN

# Get positive training data
train_pos = getPositiveImages('Training/g_r_PositiveAll', max_num, input_shape=input_shape)
print("Train Positive Shape: " + str(train_pos.shape))
excel_headers.append("Train_Positive_Shape")
excel_dictionary.append(train_pos.shape)

# Get negative training data
train_neg = getNegativeImages('Training/Negative', max_num, input_shape=input_shape)
print("Train Negative Shape: " + str(train_neg.shape))
excel_headers.append("Train_Negative_Shape")
excel_dictionary.append(train_neg.shape)

all_training_data, all_training_labels, _ = makeImageSet(train_pos, train_neg, shuffle_needed=use_shuffle)
if use_augmented_data:
    all_training_data, all_training_labels = createAugmentedData(all_training_data, all_training_labels)

training_data, val_data, training_labels, val_labels = train_test_split(all_training_data,
                                                                        all_training_labels,
                                                                        test_size=validation_split,
                                                                        shuffle=True)
excel_headers.append("All_Training_Data_Shape")
excel_dictionary.append(all_training_labels.shape)
excel_headers.append("All_Training_Labels_Shape")
excel_dictionary.append(all_training_labels.shape)
excel_headers.append("Training_Data_Shape")
excel_dictionary.append(training_data.shape)
excel_headers.append("Validation_Data_Shape")
excel_dictionary.append(val_data.shape)
excel_headers.append("Training_Labels_Shape")
excel_dictionary.append(training_labels.shape)
excel_headers.append("Validation_Labels_Shape")
excel_dictionary.append(val_labels.shape)
excel_headers.append("Validation_Split")
excel_dictionary.append(validation_split)

history, classifier = usingCnnModel(training_data,
                                    training_labels,
                                    val_data,
                                    val_labels)

classifier.load_weights('best_weights.hdf5')
classifier.save_weights('galaxies_cnn.h5')

excel_headers.append("Epochs")
excel_dictionary.append(epochs)
excel_headers.append("Batch_size")
excel_dictionary.append(batch_size)

# Plot run metrics
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
number_of_completed_epochs = range(1, len(acc) + 1)

# Accuracies
train_val_accuracy_figure = plt.figure()
plt.plot(number_of_completed_epochs, acc, label='Training acc')
plt.plot(number_of_completed_epochs, val_acc, label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.xlabel("Epochs")
plt.ylabel("Accuracy")
# plt.show()
train_val_accuracy_figure.savefig('../Results/g_r_%s/TrainingValidationAccuracy.png' % dt_string)
plt.close()

# Losses
train_val_loss_figure = plt.figure()
plt.plot(number_of_completed_epochs, loss, label='Training loss')
plt.plot(number_of_completed_epochs, val_loss, label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.xlabel("Epochs")
plt.ylabel("Loss")
# plt.show()
train_val_loss_figure.savefig('../Results/g_r_%s/TrainingValidationLoss.png' % dt_string)
plt.close()

# make positive and negative directory
if not os.path.exists('../Results/g_r_%s/PositiveResults/' % dt_string):
    os.mkdir('../Results/g_r_%s/PositiveResults/' % dt_string)

if not os.path.exists('../Results/g_r_%s/NegativeResults/' % dt_string):
    os.mkdir('../Results/g_r_%s/NegativeResults/' % dt_string)

# Plot original positive image
img_positive_tensor = getPositiveImages('Training/g_r_PositiveAll', 1, input_shape=input_shape)
positive_train_figure = plt.figure()
plt.imshow(img_positive_tensor[0])
# plt.show()
print(img_positive_tensor.shape)
positive_train_figure.savefig('../Results/g_r_%s/PositiveResults/PositiveTrainingFigure.png' % dt_string)
plt.close()

# Visualise Activations of positive image
visualiseActivations(img_positive_tensor, base_dir='../Results/g_r_%s/PositiveResults/' % dt_string)

# Plot original negative image
img_negative_tensor = getNegativeImages('Training/Negative', 1, input_shape=input_shape)
negative_train_figure = plt.figure()
plt.imshow(img_negative_tensor[0])
# plt.show()
print(img_negative_tensor.shape)
negative_train_figure.savefig('../Results/g_r_%s/NegativeResults/NegativeTrainingFigure.png' % dt_string)
plt.close()

# Visualise Activations of negative image
visualiseActivations(img_negative_tensor, base_dir='../Results/g_r_%s/NegativeResults/' % dt_string)

# Classifier evaluation
test_pos = getPositiveImages('Testing/g_r_PositiveAll', max_num_testing, input_shape)
test_neg = getNegativeImages('Testing/Negative', max_num_testing, input_shape)
testing_data, testing_labels, _ = makeImageSet(test_pos, test_neg, shuffle_needed=True)
print("Testing Data Shape:  " + str(testing_data.shape))
print("Testing Labels Shape: " + str(testing_labels.shape))
print("Got Unseen Testing data")

scores = classifier.evaluate(testing_data, testing_labels, batch_size=batch_size)
print("Test loss: %s" % scores[0])
print("Test accuracy: %s" % scores[1])

excel_headers.append("Test_Loss")
excel_dictionary.append(scores[0])
excel_headers.append("Test_Accuracy")
excel_dictionary.append(scores[1])

gettingTrueFalsePositiveNegatives(testing_data,
                                  testing_labels,
                                  text_file_path='../Results/g_r_%s/TrainingTestingResults/ActualPredictedMatrix.txt'
                                                 % dt_string,
                                  predicted_lenses_filepath='../Results/g_r_%s/TrainingTestingResults' % dt_string)

# Evaluate known 47 with negative 47
known_47_images = getUnseenData('UnseenData/Known47', max_num_prediction, input_shape=input_shape)
images_47, labels_47, des_47_names = makeImageSet(positive_images=list(known_47_images.values()),
                                                  known_des_names=list(known_47_images.keys()),
                                                  shuffle_needed=True)
print("47 Images Shape:  " + str(images_47.shape))
print("47 Labels Shape: " + str(labels_47.shape))
print("Got Unseen 47 data")

predicted_class_probabilities_47 = classifier.predict_classes(images_47, batch_size=batch_size)
lens_predicted_count_47 = np.count_nonzero(predicted_class_probabilities_47 == 1)
non_lens_predicted_count_47 = np.count_nonzero(predicted_class_probabilities_47 == 0)
print("%s/47 known images predicted" % lens_predicted_count_47)
print("%s/47 non lensed images predicted" % non_lens_predicted_count_47)

gettingTrueFalsePositiveNegatives(images_47,
                                  labels_47,
                                  text_file_path='../Results/g_r_%s/Predicted47/47_LensesPredicted.txt' % dt_string,
                                  predicted_lenses_filepath='../Results/g_r_%s/Predicted47' % dt_string)

savePredictedLenses(des_47_names,
                    predicted_class_probabilities_47,
                    predicted_lenses_filepath='../Results/g_r_%s/Predicted47' % dt_string,
                    text_file_path='../Results/g_r_%s/Predicted47/47_LensesPredicted.txt' % dt_string)

excel_headers.append("Predicted_Lens_47")
excel_dictionary.append(lens_predicted_count_47)
excel_headers.append("Predicted_No_Lens_47")
excel_dictionary.append(non_lens_predicted_count_47)

# Evaluate known 84 with negative 84
known_84_images = getUnseenData('UnseenData/Known84', max_num_prediction, input_shape=input_shape)
images_84, labels_84, des_84_names = makeImageSet(positive_images=list(known_84_images.values()),
                                                  known_des_names=list(known_84_images.keys()),
                                                  shuffle_needed=True)
print("84 Images Shape:  " + str(images_84.shape))
print("84 Labels Shape: " + str(labels_84.shape))
print("Got Unseen 84 data")

predicted_class_probabilities_84 = classifier.predict_classes(images_84, batch_size=batch_size)
lens_predicted_count_84 = np.count_nonzero(predicted_class_probabilities_84 == 1)
non_lens_predicted_count_84 = np.count_nonzero(predicted_class_probabilities_84 == 0)
print("%s/84 known images predicted" % lens_predicted_count_84)
print("%s/84 non lensed images predicted" % non_lens_predicted_count_84)

gettingTrueFalsePositiveNegatives(images_84,
                                  labels_84,
                                  text_file_path='../Results/g_r_%s/Predicted84/84_LensesPredicted.txt' % dt_string,
                                  predicted_lenses_filepath='../Results/g_r_%s/Predicted84' % dt_string)

savePredictedLenses(des_84_names,
                    predicted_class_probabilities_84,
                    predicted_lenses_filepath='../Results/g_r_%s/Predicted84' % dt_string,
                    text_file_path='../Results/g_r_%s/Predicted84/84_LensesPredicted.txt' % dt_string)

excel_headers.append("Predicted_Lens_84")
excel_dictionary.append(lens_predicted_count_84)
excel_headers.append("Predicted_No_Lens_84")
excel_dictionary.append(non_lens_predicted_count_84)

all_unseen_images = np.concatenate((images_47, images_84))
all_unseen_labels = np.concatenate((labels_47, labels_84))
all_des_names = np.concatenate((des_47_names, des_84_names))
print("All Data Shape: " + str(all_unseen_images.shape))
print("All Data Labels: " + str(all_unseen_labels.shape))

all_predicted_class_probabilities = classifier.predict_classes(all_unseen_images, batch_size=batch_size)
all_lens_predicted_count = np.count_nonzero(all_predicted_class_probabilities == 1)
all_non_lens_predicted_count = np.count_nonzero(all_predicted_class_probabilities == 0)
print("%s/131 known images predicted" % all_lens_predicted_count)
print("%s/131 non lensed images predicted" % all_non_lens_predicted_count)

gettingTrueFalsePositiveNegatives(all_unseen_images,
                                  all_unseen_labels,
                                  text_file_path='../Results/g_r_%s/PredictedAll/All_LensesPredicted.txt' % dt_string,
                                  predicted_lenses_filepath='../Results/g_r_%s/PredictedAll' % dt_string)

savePredictedLenses(all_des_names,
                    all_predicted_class_probabilities,
                    predicted_lenses_filepath='../Results/g_r_%s/PredictedAll' % dt_string,
                    text_file_path='../Results/g_r_%s/Predicted84/All_LensesPredicted.txt' % dt_string)

excel_headers.append("Predicted_Lens_All")
excel_dictionary.append(all_lens_predicted_count)
excel_headers.append("Predicted_No_Lens_All")
excel_dictionary.append(all_non_lens_predicted_count)

# K fold for training data
executeKFoldValidation(training_data, training_labels, val_data, val_labels, testing_data, testing_labels,
                       images_47, labels_47, images_84, labels_84, all_unseen_images, all_unseen_labels)

print("Test loss of normal CNN: %s" % scores[0])
print("Test accuracy of normal CNN: %s" % (scores[1] * 100))

# add row to excel table
if makeNewCSVFile:
    createExcelSheet('../Results/new_kerasCNN_Results.csv', excel_headers)
    writeToFile('../Results/new_kerasCNN_Results.csv', excel_dictionary)
else:
    writeToFile('../Results/new_kerasCNN_Results.csv', excel_dictionary)

3178a5408957afa30eb3042a259102a60f7100a5
<mask token>


def getPositiveImages(images_dir, max_num, input_shape):
    """
    This gets the positively simulated images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the positively simulated images.
        max_num(integer):   This is the number of sources of the positively simulated images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        positive_images(numpy array):   This is the numpy array of the positively simulated images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        positive_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/%s_g_norm.fits' % (os.
                path.join(root, folder), folder))
            r_img_path = get_pkg_data_filename('%s/%s_r_norm.fits' % (os.
                path.join(root, folder), folder))
            i_img_path = get_pkg_data_filename('%s/%s_i_norm.fits' % (os.
                path.join(root, folder), folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = [g_data, r_data, i_data]
            positive_images[index] = img_data
            index += 1
            if index >= num_of_images:
                break
        return positive_images.reshape(num_of_images, input_shape[0],
            input_shape[1], input_shape[2])


def getNegativeImages(images_dir, max_num, input_shape):
    """
    This gets the negative images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the negative images.
        max_num(integer):   This is the number of sources of the negative images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        negative_images(numpy array):   This is the numpy array of the negative images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        negative_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % os.path.
                join(root, folder))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % os.path.
                join(root, folder))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % os.path.
                join(root, folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = [g_data, r_data, i_data]
            negative_images[index] = img_data
            index += 1
            if index >= num_of_images:
                break
        return negative_images.reshape(num_of_images, input_shape[0],
            input_shape[1], input_shape[2])


def getUnseenData(images_dir, max_num, input_shape):
    """
        This gets the unseen images in the g, r and  i bands containing the identified known lenses.
        Args:
            images_dir(string): This is the file path address of the unseen images.
            max_num(integer):   This is the number of sources of the unseen images to be used.
            input_shape(tuple): This is the shape of the images.
        Returns:
            des_tiles(dictionary):   This is the dictionary of the unseen images with the shape of
                                            (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                            (num_of_images, 100, 100, 3).
        """
    des_tiles = {}
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % os.path.
                join(root, folder))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % os.path.
                join(root, folder))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % os.path.
                join(root, folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = np.array([g_data, r_data, i_data]).reshape(input_shape
                [0], input_shape[1], input_shape[2])
            des_tiles.update({folder: img_data})
            index += 1
            if index >= num_of_images:
                break
        return des_tiles


def makeImageSet(positive_images, negative_images=None, known_des_names=
    None, neg_des_names=None, shuffle_needed=use_shuffle):
    """
    This is used to create data set of images and labels, in which the positive and negative images are all
    combined and shuffled.
    Args:
        positive_images(numpy array):   This is the numpy array of the positively simulated images.
        negative_images(numpy array):   This is the numpy array of the negative images, this is set to a
                                        default of None.
        known_des_names(list):    This is the dictionary of the unseen known lenses, this is set to a
                                        default of None.
        neg_des_names(list):      This is the dictionary of the negative images, this is set to a
                                        default of None.
        shuffle_needed(boolean):        This is a boolean value to determine whether or not shuffling of the given data
                                        sets is required.
    Returns:
        image_set(numpy array):         This is the image data set of  numpy array of the combination positive
                                        and negative images.
        label_set(numpy array):         This is the label data set of  numpy array of the combination positive
                                        and negative label.
        des_names_set(numpy array):     This is the des name data set of the known lenses and negative images used.
    """
    image_set = []
    label_set = []
    des_names_set = []
    if known_des_names is None and neg_des_names is None:
        for index_none in range(0, len(positive_images)):
            image_set.append(positive_images[index_none])
            label_set.append(1)
        for index_none in range(0, len(negative_images)):
            image_set.append(negative_images[index_none])
            label_set.append(0)
        if shuffle_needed:
            image_set, label_set = shuffle(image_set, label_set)
    else:
        for index_des in range(0, len(positive_images)):
            image_set.append(positive_images[index_des])
            label_set.append(1)
            des_names_set.append(known_des_names[index_des])
        for index_des in range(0, len(negative_images)):
            image_set.append(negative_images[index_des])
            label_set.append(0)
            des_names_set.append(neg_des_names[index_des])
        if shuffle_needed:
            image_set, label_set, des_names_set = shuffle(image_set,
                label_set, des_names_set)
    return np.array(image_set), np.array(label_set), np.array(des_names_set)


def buildClassifier(input_shape=(100, 100, 3)):
    """
    This creates the CNN algorithm.
    Args:
        input_shape(tuple): This is the image shape of (100,100,3)
    Returns:
        classifier(sequential): This is the sequential model.
    """
    classifier = Sequential()
    classifier.add(Conv2D(32, kernel_size=(3, 3), activation='relu',
        input_shape=input_shape, padding='same'))
    classifier.add(MaxPooling2D(pool_size=(3, 3), padding='same'))
    classifier.add(Dropout(0.5))
    classifier.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Conv2D(128, (3, 3), padding='same', activation='relu'))
    classifier.add(Dropout(0.5))
    classifier.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dropout(0.2))
    classifier.add(Conv2D(512, (3, 3), padding='same', activation='relu'))
    classifier.add(Conv2D(1024, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dense(units=1024, activation='relu'))
    classifier.add(Dropout(0.2))
    classifier.add(Flatten())
    classifier.add(Dense(units=1024, activation='relu'))
    classifier.add(Dense(units=256, activation='relu'))
    classifier.add(Dropout(0.2))
    classifier.add(Dense(units=1, activation='sigmoid'))
    classifier.summary()
    classifier.compile(optimizer='adam', loss='binary_crossentropy',
        metrics=['accuracy'])
    plot_model(classifier, to_file='model_plot.png', show_shapes=True,
        show_layer_names=True)
    return classifier


<mask token>


def usingCnnModel(training_data, training_labels, val_data, val_labels):
    """
    This is using the CNN model and setting it up.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
        val_data(numpy arrays):         This is the numpy array of the validation data.
        val_labels(numpy arrays):       This is the numpy array of the validation labels.
    Returns:
        history(history):               This is the history of the classifier.
        classifier(sequential):         This is the cnn model classifier fitted to the training data and labels.
    """
    model_checkpoint = ModelCheckpoint(filepath='best_weights.hdf5',
        monitor=monitor_model_checkpoint, save_best_only=True)
    early_stopping = EarlyStopping(monitor=monitor_early_stopping, patience
        =patience_num)
    classifier = buildClassifier()
    callbacks_array = []
    if use_early_stopping:
        callbacks_array.append(early_stopping)
    if use_model_checkpoint:
        callbacks_array.append(model_checkpoint)
    print(len(training_data))
    history = classifier.fit(training_data, training_labels, epochs=epochs,
        validation_data=(val_data, val_labels), callbacks=callbacks_array,
        batch_size=batch_size)
    return history, classifier


def createAugmentedData(training_data, training_labels):
    """
    This is creates the augmented data.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
    Returns:
        complete_training_data_set(numpy array):    This is the numpy array of the total training data, which is has
                                                    undergone augmentation.
        complete_training_labels_set(numpy array):  This is the numpy array of the total training labels, which is has
                                                    undergone augmentation.
    """
    complete_training_data_set = []
    complete_training_labels_set = []
    for data in training_data:
        complete_training_data_set.append(data)
    print('Complete Training Data: ' + str(len(complete_training_data_set)))
    for label in training_labels:
        complete_training_labels_set.append(label)
    print('Complete Training Label: ' + str(len(complete_training_labels_set)))
    data_augmented = ImageDataGenerator(featurewise_center=True,
        featurewise_std_normalization=True, rotation_range=90,
        width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True,
        vertical_flip=True)
    data_augmented.fit(training_data)
    training_data_size = training_data.shape[0]
    aug_counter = 0
    while aug_counter < augmented_multiple - 1:
        iterator = data_augmented.flow(training_data, training_labels,
            batch_size=training_data_size)
        augmented_data = iterator.next()
        for data in augmented_data[0]:
            complete_training_data_set.append(data)
        for label in augmented_data[1]:
            complete_training_labels_set.append(label)
        aug_counter += 1
    print('Size of All Training Data: ' + str(len(complete_training_data_set)))
    print('Size of All Training Labels: ' + str(len(
        complete_training_labels_set)))
    array_training_data = np.array(complete_training_data_set)
    array_training_labels = np.array(complete_training_labels_set)
    print('Shape of complete training data: ' + str(array_training_data.shape))
    print('Shape of complete training labels: ' + str(array_training_labels
        .shape))
    return np.array(complete_training_data_set), np.array(
        complete_training_labels_set)


def savePredictedLenses(des_names_array, predicted_class_probabilities,
    predicted_lenses_filepath, text_file_path):
    """
    This saves the names of the predicted lenses in the respective textfiles.
    Args:
        des_names_array(numpy array): This is a list of the des names of the sources.
        predicted_class_probabilities(list):    This is a list of the probabilities in which lenses are predicted by
                                                the algorithm.
        predicted_lenses_filepath(string):      This is the string of the predicted lenses filepath, where this needs
                                                to be saved in the directory.
        text_file_path(string):                 This is the text file path address to which these images are saved.
    Saves:
        text_file(.txt file):                   This is the text file saved containing the predicted lenses DES names.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)
    text_file = open('%s' % text_file_path, 'a+')
    text_file.write('Predicted Lenses: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 1:
            text_file.write('%s \n ' % des_names_array[lens_index])
    text_file.write('\n')
    text_file.write('\n')
    text_file.write('No Lenses Predicted: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 0:
            text_file.write('%s \n ' % des_names_array[lens_index])
    text_file.close()


<mask token>


def gettingKFoldConfusionMatrix(test_data, test_labels, images_47,
    labels_47, images_84, labels_84, all_unseen_images, all_unseen_labels,
    kf_counter):
    test_confusion_matrix = gettingTrueFalsePositiveNegatives(test_data,
        test_labels, text_file_path=
        '../Results/%s/TrainingTestingResults/KFold_PredictedMatrix.txt' %
        dt_string, predicted_lenses_filepath=
        '../Results/%s/TrainingTestingResults' % dt_string, kf_counter=
        kf_counter)
    confusion_matrix_47 = gettingTrueFalsePositiveNegatives(images_47,
        labels_47, text_file_path=
        '../Results/%s/Predicted47/KFold_47_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/Predicted47' %
        dt_string, kf_counter=kf_counter)
    confusion_matrix_84 = gettingTrueFalsePositiveNegatives(images_84,
        labels_84, text_file_path=
        '../Results/%s/Predicted84/KFold_84_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/Predicted84' %
        dt_string, kf_counter=kf_counter)
    all_confusion_matrix = gettingTrueFalsePositiveNegatives(all_unseen_images,
        all_unseen_labels, text_file_path=
        '../Results/%s/All_Predicted/KFold_All_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/All_Predicted' %
        dt_string, kf_counter=kf_counter)
    return (test_confusion_matrix, confusion_matrix_47, confusion_matrix_84,
        all_confusion_matrix)


<mask token>

<mask token>


def getPositiveImages(images_dir, max_num, input_shape):
    """
    This gets the positively simulated images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the positively simulated images.
        max_num(integer):   This is the number of sources of the positively simulated images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        positive_images(numpy array):   This is the numpy array of the positively simulated images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        positive_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/%s_g_norm.fits' % (os.
                path.join(root, folder), folder))
            r_img_path = get_pkg_data_filename('%s/%s_r_norm.fits' % (os.
                path.join(root, folder), folder))
            i_img_path = get_pkg_data_filename('%s/%s_i_norm.fits' % (os.
                path.join(root, folder), folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = [g_data, r_data, i_data]
            positive_images[index] = img_data
            index += 1
            if index >= num_of_images:
                break
        return positive_images.reshape(num_of_images, input_shape[0],
            input_shape[1], input_shape[2])


def getNegativeImages(images_dir, max_num, input_shape):
    """
    This gets the negative images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the negative images.
        max_num(integer):   This is the number of sources of the negative images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        negative_images(numpy array):   This is the numpy array of the negative images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        negative_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % os.path.
                join(root, folder))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % os.path.
                join(root, folder))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % os.path.
                join(root, folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = [g_data, r_data, i_data]
            negative_images[index] = img_data
            index += 1
            if index >= num_of_images:
                break
        return negative_images.reshape(num_of_images, input_shape[0],
            input_shape[1], input_shape[2])


def getUnseenData(images_dir, max_num, input_shape):
    """
        This gets the unseen images in the g, r and  i bands containing the identified known lenses.
        Args:
            images_dir(string): This is the file path address of the unseen images.
            max_num(integer):   This is the number of sources of the unseen images to be used.
            input_shape(tuple): This is the shape of the images.
        Returns:
            des_tiles(dictionary):   This is the dictionary of the unseen images with the shape of
                                            (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                            (num_of_images, 100, 100, 3).
        """
    des_tiles = {}
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % os.path.
                join(root, folder))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % os.path.
                join(root, folder))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % os.path.
                join(root, folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = np.array([g_data, r_data, i_data]).reshape(input_shape
                [0], input_shape[1], input_shape[2])
            des_tiles.update({folder: img_data})
            index += 1
            if index >= num_of_images:
                break
        return des_tiles


def makeImageSet(positive_images, negative_images=None, known_des_names=
    None, neg_des_names=None, shuffle_needed=use_shuffle):
    """
    This is used to create data set of images and labels, in which the positive and negative images are all
    combined and shuffled.
    Args:
        positive_images(numpy array):   This is the numpy array of the positively simulated images.
        negative_images(numpy array):   This is the numpy array of the negative images, this is set to a
                                        default of None.
        known_des_names(list):    This is the dictionary of the unseen known lenses, this is set to a
                                        default of None.
        neg_des_names(list):      This is the dictionary of the negative images, this is set to a
                                        default of None.
        shuffle_needed(boolean):        This is a boolean value to determine whether or not shuffling of the given data
                                        sets is required.
    Returns:
        image_set(numpy array):         This is the image data set of  numpy array of the combination positive
                                        and negative images.
        label_set(numpy array):         This is the label data set of  numpy array of the combination positive
                                        and negative label.
        des_names_set(numpy array):     This is the des name data set of the known lenses and negative images used.
    """
    image_set = []
    label_set = []
    des_names_set = []
    if known_des_names is None and neg_des_names is None:
        for index_none in range(0, len(positive_images)):
            image_set.append(positive_images[index_none])
            label_set.append(1)
        for index_none in range(0, len(negative_images)):
            image_set.append(negative_images[index_none])
            label_set.append(0)
        if shuffle_needed:
            image_set, label_set = shuffle(image_set, label_set)
    else:
        for index_des in range(0, len(positive_images)):
            image_set.append(positive_images[index_des])
            label_set.append(1)
            des_names_set.append(known_des_names[index_des])
        for index_des in range(0, len(negative_images)):
            image_set.append(negative_images[index_des])
            label_set.append(0)
            des_names_set.append(neg_des_names[index_des])
        if shuffle_needed:
            image_set, label_set, des_names_set = shuffle(image_set,
                label_set, des_names_set)
    return np.array(image_set), np.array(label_set), np.array(des_names_set)


def buildClassifier(input_shape=(100, 100, 3)):
    """
    This creates the CNN algorithm.
    Args:
        input_shape(tuple): This is the image shape of (100,100,3)
    Returns:
        classifier(sequential): This is the sequential model.
    """
    classifier = Sequential()
    classifier.add(Conv2D(32, kernel_size=(3, 3), activation='relu',
        input_shape=input_shape, padding='same'))
    classifier.add(MaxPooling2D(pool_size=(3, 3), padding='same'))
    classifier.add(Dropout(0.5))
    classifier.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Conv2D(128, (3, 3), padding='same', activation='relu'))
    classifier.add(Dropout(0.5))
    classifier.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dropout(0.2))
    classifier.add(Conv2D(512, (3, 3), padding='same', activation='relu'))
    classifier.add(Conv2D(1024, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dense(units=1024, activation='relu'))
    classifier.add(Dropout(0.2))
    classifier.add(Flatten())
    classifier.add(Dense(units=1024, activation='relu'))
    classifier.add(Dense(units=256, activation='relu'))
    classifier.add(Dropout(0.2))
    classifier.add(Dense(units=1, activation='sigmoid'))
    classifier.summary()
    classifier.compile(optimizer='adam', loss='binary_crossentropy',
        metrics=['accuracy'])
    plot_model(classifier, to_file='model_plot.png', show_shapes=True,
        show_layer_names=True)
    return classifier


<mask token>


def usingCnnModel(training_data, training_labels, val_data, val_labels):
    """
    This is using the CNN model and setting it up.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
        val_data(numpy arrays):         This is the numpy array of the validation data.
        val_labels(numpy arrays):       This is the numpy array of the validation labels.
    Returns:
        history(history):               This is the history of the classifier.
        classifier(sequential):         This is the cnn model classifier fitted to the training data and labels.
    """
    model_checkpoint = ModelCheckpoint(filepath='best_weights.hdf5',
        monitor=monitor_model_checkpoint, save_best_only=True)
    early_stopping = EarlyStopping(monitor=monitor_early_stopping, patience
        =patience_num)
    classifier = buildClassifier()
    callbacks_array = []
    if use_early_stopping:
        callbacks_array.append(early_stopping)
    if use_model_checkpoint:
        callbacks_array.append(model_checkpoint)
    print(len(training_data))
    history = classifier.fit(training_data, training_labels, epochs=epochs,
        validation_data=(val_data, val_labels), callbacks=callbacks_array,
        batch_size=batch_size)
    return history, classifier


def createAugmentedData(training_data, training_labels):
    """
    This is creates the augmented data.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
    Returns:
        complete_training_data_set(numpy array):    This is the numpy array of the total training data, which is has
                                                    undergone augmentation.
        complete_training_labels_set(numpy array):  This is the numpy array of the total training labels, which is has
                                                    undergone augmentation.
    """
    complete_training_data_set = []
    complete_training_labels_set = []
    for data in training_data:
        complete_training_data_set.append(data)
    print('Complete Training Data: ' + str(len(complete_training_data_set)))
    for label in training_labels:
        complete_training_labels_set.append(label)
    print('Complete Training Label: ' + str(len(complete_training_labels_set)))
    data_augmented = ImageDataGenerator(featurewise_center=True,
        featurewise_std_normalization=True, rotation_range=90,
        width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True,
        vertical_flip=True)
    data_augmented.fit(training_data)
    training_data_size = training_data.shape[0]
    aug_counter = 0
    while aug_counter < augmented_multiple - 1:
        iterator = data_augmented.flow(training_data, training_labels,
            batch_size=training_data_size)
        augmented_data = iterator.next()
        for data in augmented_data[0]:
            complete_training_data_set.append(data)
        for label in augmented_data[1]:
            complete_training_labels_set.append(label)
        aug_counter += 1
    print('Size of All Training Data: ' + str(len(complete_training_data_set)))
    print('Size of All Training Labels: ' + str(len(
        complete_training_labels_set)))
    array_training_data = np.array(complete_training_data_set)
    array_training_labels = np.array(complete_training_labels_set)
    print('Shape of complete training data: ' + str(array_training_data.shape))
    print('Shape of complete training labels: ' + str(array_training_labels
        .shape))
    return np.array(complete_training_data_set), np.array(
        complete_training_labels_set)


def savePredictedLenses(des_names_array, predicted_class_probabilities,
    predicted_lenses_filepath, text_file_path):
    """
    This saves the names of the predicted lenses in the respective textfiles.
    Args:
        des_names_array(numpy array): This is a list of the des names of the sources.
        predicted_class_probabilities(list):    This is a list of the probabilities in which lenses are predicted by
                                                the algorithm.
        predicted_lenses_filepath(string):      This is the string of the predicted lenses filepath, where this needs
                                                to be saved in the directory.
        text_file_path(string):                 This is the text file path address to which these images are saved.
    Saves:
        text_file(.txt file):                   This is the text file saved containing the predicted lenses DES names.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)
    text_file = open('%s' % text_file_path, 'a+')
    text_file.write('Predicted Lenses: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 1:
            text_file.write('%s \n ' % des_names_array[lens_index])
    text_file.write('\n')
    text_file.write('\n')
    text_file.write('No Lenses Predicted: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 0:
            text_file.write('%s \n ' % des_names_array[lens_index])
    text_file.close()


def gettingTrueFalsePositiveNegatives(testing_data, testing_labels,
    text_file_path, predicted_lenses_filepath, kf_counter=0):
    """
    This is used to get the True/False Positive and Negative values gained from the CNN confusion matrix.
    Args:
        testing_data(numpy array):          This is the unseen testing data numpy array.
        testing_labels(numpy array):        This is the unseen testing label numpy array.
        text_file_path(string):             This is the file path name of the text file in which the confusion
                                            matrix is saved.
        predicted_lenses_filepath(string):  This is the file path in which the text file is saved.
    Saves:
        This saves a confusion matrix of the True/False Positive and Negative values.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)
    predicted_data = classifier.predict_classes(testing_data)
    true_negative, false_positive, false_negative, true_positive = (
        confusion_matrix(testing_labels, predicted_data.round()).ravel())
    matrix = confusion_matrix(testing_labels, predicted_data.round())
    print(str(matrix) + ' \n ')
    print('True Positive: %s \n' % true_positive)
    print('False Negative: %s \n' % false_negative)
    print('False Positive: %s \n' % false_positive)
    print('True Negative: %s \n' % true_negative)
    text_file = open('%s' % text_file_path, 'a+')
    text_file.write('KFold Number: %s \n' % str(kf_counter))
    text_file.write('Predicted vs True Matrix: \n')
    text_file.write(str(matrix) + ' \n ')
    text_file.write('True Negative: %s \n' % str(true_negative))
    text_file.write('False Positive: %s \n' % str(false_positive))
    text_file.write('False Negative: %s \n' % str(false_negative))
    text_file.write('True Positive: %s \n' % str(true_positive))
    text_file.write('\n')
    text_file.close()
    confusion_matrix_array = [true_negative, false_positive, false_negative,
        true_positive]
    return confusion_matrix_array


def gettingKFoldConfusionMatrix(test_data, test_labels, images_47,
    labels_47, images_84, labels_84, all_unseen_images, all_unseen_labels,
    kf_counter):
    test_confusion_matrix = gettingTrueFalsePositiveNegatives(test_data,
        test_labels, text_file_path=
        '../Results/%s/TrainingTestingResults/KFold_PredictedMatrix.txt' %
        dt_string, predicted_lenses_filepath=
        '../Results/%s/TrainingTestingResults' % dt_string, kf_counter=
        kf_counter)
    confusion_matrix_47 = gettingTrueFalsePositiveNegatives(images_47,
        labels_47, text_file_path=
        '../Results/%s/Predicted47/KFold_47_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/Predicted47' %
        dt_string, kf_counter=kf_counter)
    confusion_matrix_84 = gettingTrueFalsePositiveNegatives(images_84,
        labels_84, text_file_path=
        '../Results/%s/Predicted84/KFold_84_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/Predicted84' %
        dt_string, kf_counter=kf_counter)
    all_confusion_matrix = gettingTrueFalsePositiveNegatives(all_unseen_images,
        all_unseen_labels, text_file_path=
        '../Results/%s/All_Predicted/KFold_All_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/All_Predicted' %
        dt_string, kf_counter=kf_counter)
    return (test_confusion_matrix, confusion_matrix_47, confusion_matrix_84,
        all_confusion_matrix)


<mask token>

<mask token>


def getPositiveImages(images_dir, max_num, input_shape):
    """
    This gets the positively simulated images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the positively simulated images.
        max_num(integer):   This is the number of sources of the positively simulated images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        positive_images(numpy array):   This is the numpy array of the positively simulated images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        positive_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/%s_g_norm.fits' % (os.
                path.join(root, folder), folder))
            r_img_path = get_pkg_data_filename('%s/%s_r_norm.fits' % (os.
                path.join(root, folder), folder))
            i_img_path = get_pkg_data_filename('%s/%s_i_norm.fits' % (os.
                path.join(root, folder), folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = [g_data, r_data, i_data]
            positive_images[index] = img_data
            index += 1
            if index >= num_of_images:
                break
        return positive_images.reshape(num_of_images, input_shape[0],
            input_shape[1], input_shape[2])


def getNegativeImages(images_dir, max_num, input_shape):
    """
    This gets the negative images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the negative images.
        max_num(integer):   This is the number of sources of the negative images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        negative_images(numpy array):   This is the numpy array of the negative images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        negative_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % os.path.
                join(root, folder))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % os.path.
                join(root, folder))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % os.path.
                join(root, folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = [g_data, r_data, i_data]
            negative_images[index] = img_data
            index += 1
            if index >= num_of_images:
                break
        return negative_images.reshape(num_of_images, input_shape[0],
            input_shape[1], input_shape[2])


def getUnseenData(images_dir, max_num, input_shape):
    """
        This gets the unseen images in the g, r and  i bands containing the identified known lenses.
        Args:
            images_dir(string): This is the file path address of the unseen images.
            max_num(integer):   This is the number of sources of the unseen images to be used.
            input_shape(tuple): This is the shape of the images.
        Returns:
            des_tiles(dictionary):   This is the dictionary of the unseen images with the shape of
                                            (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                            (num_of_images, 100, 100, 3).
        """
    des_tiles = {}
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % os.path.
                join(root, folder))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % os.path.
                join(root, folder))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % os.path.
                join(root, folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = np.array([g_data, r_data, i_data]).reshape(input_shape
                [0], input_shape[1], input_shape[2])
            des_tiles.update({folder: img_data})
            index += 1
            if index >= num_of_images:
                break
        return des_tiles


def makeImageSet(positive_images, negative_images=None, known_des_names=
    None, neg_des_names=None, shuffle_needed=use_shuffle):
    """
    This is used to create data set of images and labels, in which the positive and negative images are all
    combined and shuffled.
    Args:
        positive_images(numpy array):   This is the numpy array of the positively simulated images.
        negative_images(numpy array):   This is the numpy array of the negative images, this is set to a
                                        default of None.
        known_des_names(list):    This is the dictionary of the unseen known lenses, this is set to a
                                        default of None.
        neg_des_names(list):      This is the dictionary of the negative images, this is set to a
                                        default of None.
        shuffle_needed(boolean):        This is a boolean value to determine whether or not shuffling of the given data
                                        sets is required.
    Returns:
        image_set(numpy array):         This is the image data set of  numpy array of the combination positive
                                        and negative images.
        label_set(numpy array):         This is the label data set of  numpy array of the combination positive
                                        and negative label.
        des_names_set(numpy array):     This is the des name data set of the known lenses and negative images used.
    """
    image_set = []
    label_set = []
    des_names_set = []
    if known_des_names is None and neg_des_names is None:
        for index_none in range(0, len(positive_images)):
            image_set.append(positive_images[index_none])
            label_set.append(1)
        for index_none in range(0, len(negative_images)):
            image_set.append(negative_images[index_none])
            label_set.append(0)
        if shuffle_needed:
            image_set, label_set = shuffle(image_set, label_set)
    else:
        for index_des in range(0, len(positive_images)):
            image_set.append(positive_images[index_des])
            label_set.append(1)
            des_names_set.append(known_des_names[index_des])
        for index_des in range(0, len(negative_images)):
            image_set.append(negative_images[index_des])
            label_set.append(0)
            des_names_set.append(neg_des_names[index_des])
        if shuffle_needed:
            image_set, label_set, des_names_set = shuffle(image_set,
                label_set, des_names_set)
    return np.array(image_set), np.array(label_set), np.array(des_names_set)


def buildClassifier(input_shape=(100, 100, 3)):
    """
    This creates the CNN algorithm.
    Args:
        input_shape(tuple): This is the image shape of (100,100,3)
    Returns:
        classifier(sequential): This is the sequential model.
    """
    classifier = Sequential()
    classifier.add(Conv2D(32, kernel_size=(3, 3), activation='relu',
        input_shape=input_shape, padding='same'))
    classifier.add(MaxPooling2D(pool_size=(3, 3), padding='same'))
    classifier.add(Dropout(0.5))
    classifier.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Conv2D(128, (3, 3), padding='same', activation='relu'))
    classifier.add(Dropout(0.5))
    classifier.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dropout(0.2))
    classifier.add(Conv2D(512, (3, 3), padding='same', activation='relu'))
    classifier.add(Conv2D(1024, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dense(units=1024, activation='relu'))
    classifier.add(Dropout(0.2))
    classifier.add(Flatten())
    classifier.add(Dense(units=1024, activation='relu'))
    classifier.add(Dense(units=256, activation='relu'))
    classifier.add(Dropout(0.2))
    classifier.add(Dense(units=1, activation='sigmoid'))
    classifier.summary()
    classifier.compile(optimizer='adam', loss='binary_crossentropy',
        metrics=['accuracy'])
    plot_model(classifier, to_file='model_plot.png', show_shapes=True,
        show_layer_names=True)
    return classifier


def visualiseActivations(img_tensor, base_dir):
    """
    This makes images of the activations, as the selected image passed through the model
    Args:
        img_tensor(numpy array):    This is the numpy array of the selected image
        base_dir(string):           This is the file path name
    Saves:
        This saves the activation images of the selected source.
    """
    global predicted_class, size
    predicted_class = classifier.predict_classes(img_tensor, batch_size=10)
    print('Predicted class is: ', predicted_class)
    layer_outputs = [layer.output for layer in classifier.layers[:12]]
    activation_model = Model(inputs=classifier.input, outputs=layer_outputs)
    activations = activation_model.predict(img_tensor)
    layer_names = []
    for layer in classifier.layers[:12]:
        layer_names.append(layer.name)
    images_per_row = 3
    count = 0
    for layer_name, layer_activation in zip(layer_names, activations):
        number_of_features = layer_activation.shape[-1]
        size = layer_activation.shape[1]
        number_of_columns = number_of_features // images_per_row
        display_grid = np.zeros((size * number_of_columns, images_per_row *
            size))
        for col in range(number_of_columns):
            for row in range(images_per_row):
                channel_image = layer_activation[0, :, :, col *
                    images_per_row + row]
                channel_image -= channel_image.mean()
                channel_image /= channel_image.std()
                channel_image *= 64
                channel_image += 128
                channel_image = np.clip(channel_image, 0, 255).astype('uint8')
                display_grid[col * size:(col + 1) * size, row * size:(row +
                    1) * size] = channel_image
        scale = 1.0 / size
        activations_figure = plt.figure(figsize=(scale * display_grid.shape
            [1], scale * display_grid.shape[0]))
        plt.title(layer_name)
        plt.grid(False)
        plt.imshow(display_grid, aspect='auto', cmap='viridis')
        activations_figure.savefig('%s/%s_Activation_%s.png' % (base_dir,
            count, layer_name))
        plt.close()
        count += 1


def usingCnnModel(training_data, training_labels, val_data, val_labels):
    """
    This is using the CNN model and setting it up.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
        val_data(numpy arrays):         This is the numpy array of the validation data.
        val_labels(numpy arrays):       This is the numpy array of the validation labels.
    Returns:
        history(history):               This is the history of the classifier.
        classifier(sequential):         This is the cnn model classifier fitted to the training data and labels.
    """
    model_checkpoint = ModelCheckpoint(filepath='best_weights.hdf5',
        monitor=monitor_model_checkpoint, save_best_only=True)
    early_stopping = EarlyStopping(monitor=monitor_early_stopping, patience
        =patience_num)
    classifier = buildClassifier()
    callbacks_array = []
    if use_early_stopping:
        callbacks_array.append(early_stopping)
    if use_model_checkpoint:
        callbacks_array.append(model_checkpoint)
    print(len(training_data))
    history = classifier.fit(training_data, training_labels, epochs=epochs,
        validation_data=(val_data, val_labels), callbacks=callbacks_array,
        batch_size=batch_size)
    return history, classifier


def createAugmentedData(training_data, training_labels):
    """
    This is creates the augmented data.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
    Returns:
        complete_training_data_set(numpy array):    This is the numpy array of the total training data, which is has
                                                    undergone augmentation.
        complete_training_labels_set(numpy array):  This is the numpy array of the total training labels, which is has
                                                    undergone augmentation.
    """
    complete_training_data_set = []
    complete_training_labels_set = []
    for data in training_data:
        complete_training_data_set.append(data)
    print('Complete Training Data: ' + str(len(complete_training_data_set)))
    for label in training_labels:
        complete_training_labels_set.append(label)
    print('Complete Training Label: ' + str(len(complete_training_labels_set)))
    data_augmented = ImageDataGenerator(featurewise_center=True,
        featurewise_std_normalization=True, rotation_range=90,
        width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True,
        vertical_flip=True)
    data_augmented.fit(training_data)
    training_data_size = training_data.shape[0]
    aug_counter = 0
    while aug_counter < augmented_multiple - 1:
        iterator = data_augmented.flow(training_data, training_labels,
            batch_size=training_data_size)
        augmented_data = iterator.next()
        for data in augmented_data[0]:
            complete_training_data_set.append(data)
        for label in augmented_data[1]:
            complete_training_labels_set.append(label)
        aug_counter += 1
    print('Size of All Training Data: ' + str(len(complete_training_data_set)))
    print('Size of All Training Labels: ' + str(len(
        complete_training_labels_set)))
    array_training_data = np.array(complete_training_data_set)
    array_training_labels = np.array(complete_training_labels_set)
    print('Shape of complete training data: ' + str(array_training_data.shape))
    print('Shape of complete training labels: ' + str(array_training_labels
        .shape))
    return np.array(complete_training_data_set), np.array(
        complete_training_labels_set)


def savePredictedLenses(des_names_array, predicted_class_probabilities,
    predicted_lenses_filepath, text_file_path):
    """
    This saves the names of the predicted lenses in the respective textfiles.
    Args:
        des_names_array(numpy array): This is a list of the des names of the sources.
        predicted_class_probabilities(list):    This is a list of the probabilities in which lenses are predicted by
                                                the algorithm.
        predicted_lenses_filepath(string):      This is the string of the predicted lenses filepath, where this needs
                                                to be saved in the directory.
        text_file_path(string):                 This is the text file path address to which these images are saved.
    Saves:
        text_file(.txt file):                   This is the text file saved containing the predicted lenses DES names.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)
    text_file = open('%s' % text_file_path, 'a+')
    text_file.write('Predicted Lenses: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 1:
            text_file.write('%s \n ' % des_names_array[lens_index])
    text_file.write('\n')
    text_file.write('\n')
    text_file.write('No Lenses Predicted: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 0:
            text_file.write('%s \n ' % des_names_array[lens_index])
    text_file.close()


def gettingTrueFalsePositiveNegatives(testing_data, testing_labels,
    text_file_path, predicted_lenses_filepath, kf_counter=0):
    """
    This is used to get the True/False Positive and Negative values gained from the CNN confusion matrix.
    Args:
        testing_data(numpy array):          This is the unseen testing data numpy array.
        testing_labels(numpy array):        This is the unseen testing label numpy array.
        text_file_path(string):             This is the file path name of the text file in which the confusion
                                            matrix is saved.
        predicted_lenses_filepath(string):  This is the file path in which the text file is saved.
    Saves:
        This saves a confusion matrix of the True/False Positive and Negative values.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)
    predicted_data = classifier.predict_classes(testing_data)
    true_negative, false_positive, false_negative, true_positive = (
        confusion_matrix(testing_labels, predicted_data.round()).ravel())
    matrix = confusion_matrix(testing_labels, predicted_data.round())
    print(str(matrix) + ' \n ')
    print('True Positive: %s \n' % true_positive)
    print('False Negative: %s \n' % false_negative)
    print('False Positive: %s \n' % false_positive)
    print('True Negative: %s \n' % true_negative)
    text_file = open('%s' % text_file_path, 'a+')
    text_file.write('KFold Number: %s \n' % str(kf_counter))
    text_file.write('Predicted vs True Matrix: \n')
    text_file.write(str(matrix) + ' \n ')
    text_file.write('True Negative: %s \n' % str(true_negative))
    text_file.write('False Positive: %s \n' % str(false_positive))
    text_file.write('False Negative: %s \n' % str(false_negative))
    text_file.write('True Positive: %s \n' % str(true_positive))
    text_file.write('\n')
    text_file.close()
    confusion_matrix_array = [true_negative, false_positive, false_negative,
        true_positive]
    return confusion_matrix_array


def gettingKFoldConfusionMatrix(test_data, test_labels, images_47,
    labels_47, images_84, labels_84, all_unseen_images, all_unseen_labels,
    kf_counter):
    test_confusion_matrix = gettingTrueFalsePositiveNegatives(test_data,
        test_labels, text_file_path=
        '../Results/%s/TrainingTestingResults/KFold_PredictedMatrix.txt' %
        dt_string, predicted_lenses_filepath=
        '../Results/%s/TrainingTestingResults' % dt_string, kf_counter=
        kf_counter)
    confusion_matrix_47 = gettingTrueFalsePositiveNegatives(images_47,
        labels_47, text_file_path=
        '../Results/%s/Predicted47/KFold_47_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/Predicted47' %
        dt_string, kf_counter=kf_counter)
    confusion_matrix_84 = gettingTrueFalsePositiveNegatives(images_84,
        labels_84, text_file_path=
        '../Results/%s/Predicted84/KFold_84_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/Predicted84' %
        dt_string, kf_counter=kf_counter)
    all_confusion_matrix = gettingTrueFalsePositiveNegatives(all_unseen_images,
        all_unseen_labels, text_file_path=
        '../Results/%s/All_Predicted/KFold_All_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/All_Predicted' %
        dt_string, kf_counter=kf_counter)
    return (test_confusion_matrix, confusion_matrix_47, confusion_matrix_84,
        all_confusion_matrix)


def executeKFoldValidation(train_data, train_labels, val_data, val_labels,
    test_data, test_labels, images_47, labels_47, images_84, labels_84,
    all_unseen_images, all_unseen_labels):
    """
    This does the k fold cross validation which is tested against the unseen testing and known lenses.
    Args:
        train_data(numpy arrays):           This is the numpy array of the training data.
        train_labels(numpy arrays):         This is the numpy array of the training labels.
        val_data(numpy arrays):             This is the numpy array of the validation data.
        val_labels(numpy arrays):           This is the numpy array of the validation labels.
        testing_data(numpy array):          This is the numpy array of the unseen testing data.
        testing_labels(numpy array):        This is the numpy array of the unseen testing label.
        images_47(numpy array):             This is the numpy array of the unseen DES images data.
        labels_47(numpy array):             This is the numpy array of the unseen DES images labels.
        images_84(numpy array):             This is the numpy array of the unseen Jacobs images data.
        labels_84(numpy array):             This is the numpy array of the unseen Jacobs images labels.
        all_unseen_images(numpy array):     This is the numpy array of the unseen DES + Jacobs images data.
        all_unseen_labels(numpy array):     This is the numpy array of the unseen DES + Jacobs images labels.

    Saves:
        This saves the scores, mean and std. of the unseen data that is evaluated in the k fold cross validation.
    """
    if run_k_fold_validation:
        print('In executingKFoldValidation')
        kfold = StratifiedKFold(n_splits=k_fold_num, shuffle=True)
        test_scores_list = []
        unseen_47_scores_list = []
        unseen_84_scores_list = []
        all_unseen_scores_list = []
        test_matrix_list = []
        matrix_47_list = []
        matrix_84_list = []
        all_matrix_list = []
        kf_counter = 0
        for train, test in kfold.split(train_data, train_labels):
            kf_counter += 1
            print('KFold #:', kf_counter)
            model = buildClassifier()
            model.fit(train_data[train], train_labels[train], epochs=epochs,
                validation_data=(val_data, val_labels), batch_size=batch_size)
            unseen_47_scores = model.evaluate(images_47, labels_47,
                batch_size=batch_size)
            unseen_47_scores_list.append(unseen_47_scores[1] * 100)
            unseen_84_scores = model.evaluate(images_84, labels_84,
                batch_size=batch_size)
            unseen_84_scores_list.append(unseen_84_scores[1] * 100)
            test_scores = model.evaluate(test_data, test_labels, batch_size
                =batch_size)
            test_scores_list.append(test_scores[1] * 100)
            all_unseen_score = model.evaluate(all_unseen_images,
                all_unseen_labels, batch_size=batch_size)
            all_unseen_scores_list.append(all_unseen_score[1] * 100)
            (test_confusion_matrix, confusion_matrix_47,
                confusion_matrix_84, all_confusion_matrix) = (
                gettingKFoldConfusionMatrix(test_data, test_labels,
                images_47, labels_47, images_84, labels_84,
                all_unseen_images, all_unseen_labels, kf_counter))
            test_matrix_list.append(test_confusion_matrix)
            matrix_47_list.append(confusion_matrix_47)
            matrix_84_list.append(confusion_matrix_84)
            all_matrix_list.append(all_confusion_matrix)
        test_scores_mean = np.mean(test_scores_list)
        test_scores_std = np.std(test_scores_list)
        unseen_47_mean = np.mean(unseen_47_scores_list)
        unseen_47_std = np.std(unseen_47_scores_list)
        unseen_84_mean = np.mean(unseen_84_scores_list)
        unseen_84_std = np.std(unseen_84_scores_list)
        all_unseen_mean = np.mean(all_unseen_scores_list)
        all_unseen_std = np.std(all_unseen_scores_list)
        print('Test Scores: ' + str(test_scores_list))
        print('Test Scores Mean: ' + str(test_scores_mean))
        print('Test Scores Std: ' + str(test_scores_std))
        print('Unseen 47 Scores: ' + str(unseen_47_scores_list))
        print('Unseen 47 Scores Mean: ' + str(unseen_47_mean))
        print('Unseen 47 Scores Std: ' + str(unseen_47_std))
        print('Unseen 84 Scores: ' + str(unseen_84_scores_list))
        print('Unseen 84 Scores Mean: ' + str(unseen_84_mean))
        print('Unseen 84 Scores Std: ' + str(unseen_84_std))
        print('All Unseen Scores: ' + str(all_unseen_scores_list))
        print('All Unseen Scores Mean: ' + str(all_unseen_mean))
        print('All Unseen Scores Std: ' + str(all_unseen_std))
        print('Test Confusion Matrices: ' + str(test_matrix_list))
        print('47 Confusion Matrices: ' + str(matrix_47_list))
        print('84 Confusion Matrices: ' + str(matrix_84_list))
        print('All Confusion Matrices: ' + str(all_matrix_list))
        excel_headers.append('Test Scores Mean')
        excel_dictionary.append(test_scores_mean)
        excel_headers.append('Test Scores Std')
        excel_dictionary.append(test_scores_std)
        excel_headers.append('Unseen 47 Scores Mean')
        excel_dictionary.append(unseen_47_mean)
        excel_headers.append('Unseen 47 Scores Std')
        excel_dictionary.append(unseen_47_std)
        excel_headers.append('Unseen 84 Scores Mean')
        excel_dictionary.append(unseen_84_mean)
        excel_headers.append('Unseen 84 Scores Std')
        excel_dictionary.append(unseen_84_std)
        excel_headers.append('All Unseen Scores Mean')
        excel_dictionary.append(all_unseen_mean)
        excel_headers.append('All Unseen Scores Std')
        excel_dictionary.append(all_unseen_std)
        plt.plot(test_scores_list, color='red', label='Testing Scores')
        plt.plot(unseen_47_scores_list, color='blue', label='Unseen 47 Scores')
        plt.plot(unseen_84_scores_list, color='black', label='Unseen 84 Scores'
            )
        plt.plot(all_unseen_scores_list, color='green', label='Unseen Scores')
        plt.xlabel('Folds')
        plt.ylabel('Accuracy')
        plt.legend()
        plt.show()


<mask token>

<mask token>
print(tensorflow.__version__)
now = datetime.now()
dt_string = now.strftime('%d_%m_%Y_%H_%M_%S')
print(dt_string)
excel_headers = []
excel_dictionary = []
excel_headers.append('Date and Time')
excel_dictionary.append(dt_string)
makeNewCSVFile = True
max_num = sys.maxsize
max_num_testing = sys.maxsize
max_num_prediction = sys.maxsize
validation_split = 0.2
k_fold_num = 5
epochs = 5
batch_size = 128
run_k_fold_validation = True
input_shape = 100, 100, 3
augmented_multiple = 2
use_augmented_data = True
patience_num = 10
use_early_stopping = True
use_model_checkpoint = True
monitor_early_stopping = 'val_loss'
monitor_model_checkpoint = 'val_acc'
use_shuffle = True
excel_headers.append('Max Training Num')
excel_dictionary.append(max_num)
excel_headers.append('Max Testing Num')
excel_dictionary.append(max_num_testing)
excel_headers.append('Max Prediction Num')
excel_dictionary.append(max_num_prediction)
excel_headers.append('Validation Split')
excel_dictionary.append(validation_split)
excel_headers.append('K fold Num')
excel_dictionary.append(k_fold_num)
excel_headers.append('Epochs')
excel_dictionary.append(epochs)
excel_headers.append('Batch Size')
excel_dictionary.append(batch_size)
excel_headers.append('Run K fold')
excel_dictionary.append(run_k_fold_validation)
excel_headers.append('Input Shape')
excel_dictionary.append(input_shape)
excel_headers.append('Augmented Multiple')
excel_dictionary.append(augmented_multiple)
excel_headers.append('Use Augmented Data')
excel_dictionary.append(use_augmented_data)
excel_headers.append('Patience')
excel_dictionary.append(patience_num)
excel_headers.append('Use Early Stopping')
excel_dictionary.append(use_early_stopping)
excel_headers.append('Use Model Checkpoint')
excel_dictionary.append(use_model_checkpoint)
excel_headers.append('Monitor Early Stopping')
excel_dictionary.append(monitor_early_stopping)
excel_headers.append('Monitor Model Checkpoint')
excel_dictionary.append(monitor_model_checkpoint)
if not os.path.exists('../Results/g_r_%s/' % dt_string):
    os.mkdir('../Results/g_r_%s/' % dt_string)


def getPositiveImages(images_dir, max_num, input_shape):
    """
    This gets the positively simulated images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the positively simulated images.
        max_num(integer):   This is the number of sources of the positively simulated images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        positive_images(numpy array):   This is the numpy array of the positively simulated images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        positive_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/%s_g_norm.fits' % (os.
                path.join(root, folder), folder))
            r_img_path = get_pkg_data_filename('%s/%s_r_norm.fits' % (os.
                path.join(root, folder), folder))
            i_img_path = get_pkg_data_filename('%s/%s_i_norm.fits' % (os.
                path.join(root, folder), folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = [g_data, r_data, i_data]
            positive_images[index] = img_data
            index += 1
            if index >= num_of_images:
                break
        return positive_images.reshape(num_of_images, input_shape[0],
            input_shape[1], input_shape[2])


def getNegativeImages(images_dir, max_num, input_shape):
    """
    This gets the negative images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the negative images.
        max_num(integer):   This is the number of sources of the negative images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        negative_images(numpy array):   This is the numpy array of the negative images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        negative_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % os.path.
                join(root, folder))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % os.path.
                join(root, folder))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % os.path.
                join(root, folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = [g_data, r_data, i_data]
            negative_images[index] = img_data
            index += 1
            if index >= num_of_images:
                break
        return negative_images.reshape(num_of_images, input_shape[0],
            input_shape[1], input_shape[2])


def getUnseenData(images_dir, max_num, input_shape):
    """
        This gets the unseen images in the g, r and  i bands containing the identified known lenses.
        Args:
            images_dir(string): This is the file path address of the unseen images.
            max_num(integer):   This is the number of sources of the unseen images to be used.
            input_shape(tuple): This is the shape of the images.
        Returns:
            des_tiles(dictionary):   This is the dictionary of the unseen images with the shape of
                                            (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                            (num_of_images, 100, 100, 3).
        """
    des_tiles = {}
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % os.path.
                join(root, folder))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % os.path.
                join(root, folder))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % os.path.
                join(root, folder))
            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]
            img_data = np.array([g_data, r_data, i_data]).reshape(input_shape
                [0], input_shape[1], input_shape[2])
            des_tiles.update({folder: img_data})
            index += 1
            if index >= num_of_images:
                break
        return des_tiles


def makeImageSet(positive_images, negative_images=None, known_des_names=
    None, neg_des_names=None, shuffle_needed=use_shuffle):
    """
    This is used to create data set of images and labels, in which the positive and negative images are all
    combined and shuffled.
    Args:
        positive_images(numpy array):   This is the numpy array of the positively simulated images.
        negative_images(numpy array):   This is the numpy array of the negative images, this is set to a
                                        default of None.
        known_des_names(list):    This is the dictionary of the unseen known lenses, this is set to a
                                        default of None.
        neg_des_names(list):      This is the dictionary of the negative images, this is set to a
                                        default of None.
        shuffle_needed(boolean):        This is a boolean value to determine whether or not shuffling of the given data
                                        sets is required.
    Returns:
        image_set(numpy array):         This is the image data set of  numpy array of the combination positive
                                        and negative images.
        label_set(numpy array):         This is the label data set of  numpy array of the combination positive
                                        and negative label.
        des_names_set(numpy array):     This is the des name data set of the known lenses and negative images used.
    """
    image_set = []
    label_set = []
    des_names_set = []
    if known_des_names is None and neg_des_names is None:
        for index_none in range(0, len(positive_images)):
            image_set.append(positive_images[index_none])
            label_set.append(1)
        for index_none in range(0, len(negative_images)):
            image_set.append(negative_images[index_none])
            label_set.append(0)
        if shuffle_needed:
            image_set, label_set = shuffle(image_set, label_set)
    else:
        for index_des in range(0, len(positive_images)):
            image_set.append(positive_images[index_des])
            label_set.append(1)
            des_names_set.append(known_des_names[index_des])
        for index_des in range(0, len(negative_images)):
            image_set.append(negative_images[index_des])
            label_set.append(0)
            des_names_set.append(neg_des_names[index_des])
        if shuffle_needed:
            image_set, label_set, des_names_set = shuffle(image_set,
                label_set, des_names_set)
    return np.array(image_set), np.array(label_set), np.array(des_names_set)


def buildClassifier(input_shape=(100, 100, 3)):
    """
    This creates the CNN algorithm.
    Args:
        input_shape(tuple): This is the image shape of (100,100,3)
    Returns:
        classifier(sequential): This is the sequential model.
    """
    classifier = Sequential()
    classifier.add(Conv2D(32, kernel_size=(3, 3), activation='relu',
        input_shape=input_shape, padding='same'))
    classifier.add(MaxPooling2D(pool_size=(3, 3), padding='same'))
    classifier.add(Dropout(0.5))
    classifier.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Conv2D(128, (3, 3), padding='same', activation='relu'))
    classifier.add(Dropout(0.5))
    classifier.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dropout(0.2))
    classifier.add(Conv2D(512, (3, 3), padding='same', activation='relu'))
    classifier.add(Conv2D(1024, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dense(units=1024, activation='relu'))
    classifier.add(Dropout(0.2))
    classifier.add(Flatten())
    classifier.add(Dense(units=1024, activation='relu'))
    classifier.add(Dense(units=256, activation='relu'))
    classifier.add(Dropout(0.2))
    classifier.add(Dense(units=1, activation='sigmoid'))
    classifier.summary()
    classifier.compile(optimizer='adam', loss='binary_crossentropy',
        metrics=['accuracy'])
    plot_model(classifier, to_file='model_plot.png', show_shapes=True,
        show_layer_names=True)
    return classifier


def visualiseActivations(img_tensor, base_dir):
    """
    This makes images of the activations, as the selected image passed through the model
    Args:
        img_tensor(numpy array):    This is the numpy array of the selected image
        base_dir(string):           This is the file path name
    Saves:
        This saves the activation images of the selected source.
    """
    global predicted_class, size
    predicted_class = classifier.predict_classes(img_tensor, batch_size=10)
    print('Predicted class is: ', predicted_class)
    layer_outputs = [layer.output for layer in classifier.layers[:12]]
    activation_model = Model(inputs=classifier.input, outputs=layer_outputs)
    activations = activation_model.predict(img_tensor)
    layer_names = []
    for layer in classifier.layers[:12]:
        layer_names.append(layer.name)
    images_per_row = 3
    count = 0
    for layer_name, layer_activation in zip(layer_names, activations):
        number_of_features = layer_activation.shape[-1]
        size = layer_activation.shape[1]
        number_of_columns = number_of_features // images_per_row
        display_grid = np.zeros((size * number_of_columns, images_per_row *
            size))
        for col in range(number_of_columns):
            for row in range(images_per_row):
                channel_image = layer_activation[0, :, :, col *
                    images_per_row + row]
                channel_image -= channel_image.mean()
                channel_image /= channel_image.std()
                channel_image *= 64
                channel_image += 128
                channel_image = np.clip(channel_image, 0, 255).astype('uint8')
                display_grid[col * size:(col + 1) * size, row * size:(row +
                    1) * size] = channel_image
        scale = 1.0 / size
        activations_figure = plt.figure(figsize=(scale * display_grid.shape
            [1], scale * display_grid.shape[0]))
        plt.title(layer_name)
        plt.grid(False)
        plt.imshow(display_grid, aspect='auto', cmap='viridis')
        activations_figure.savefig('%s/%s_Activation_%s.png' % (base_dir,
            count, layer_name))
        plt.close()
        count += 1


def usingCnnModel(training_data, training_labels, val_data, val_labels):
    """
    This is using the CNN model and setting it up.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
        val_data(numpy arrays):         This is the numpy array of the validation data.
        val_labels(numpy arrays):       This is the numpy array of the validation labels.
    Returns:
        history(history):               This is the history of the classifier.
        classifier(sequential):         This is the cnn model classifier fitted to the training data and labels.
    """
    model_checkpoint = ModelCheckpoint(filepath='best_weights.hdf5',
        monitor=monitor_model_checkpoint, save_best_only=True)
    early_stopping = EarlyStopping(monitor=monitor_early_stopping, patience
        =patience_num)
    classifier = buildClassifier()
    callbacks_array = []
    if use_early_stopping:
        callbacks_array.append(early_stopping)
    if use_model_checkpoint:
        callbacks_array.append(model_checkpoint)
    print(len(training_data))
    history = classifier.fit(training_data, training_labels, epochs=epochs,
        validation_data=(val_data, val_labels), callbacks=callbacks_array,
        batch_size=batch_size)
    return history, classifier


def createAugmentedData(training_data, training_labels):
    """
    This is creates the augmented data.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
    Returns:
        complete_training_data_set(numpy array):    This is the numpy array of the total training data, which is has
                                                    undergone augmentation.
        complete_training_labels_set(numpy array):  This is the numpy array of the total training labels, which is has
                                                    undergone augmentation.
    """
    complete_training_data_set = []
    complete_training_labels_set = []
    for data in training_data:
        complete_training_data_set.append(data)
    print('Complete Training Data: ' + str(len(complete_training_data_set)))
    for label in training_labels:
        complete_training_labels_set.append(label)
    print('Complete Training Label: ' + str(len(complete_training_labels_set)))
    data_augmented = ImageDataGenerator(featurewise_center=True,
        featurewise_std_normalization=True, rotation_range=90,
        width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True,
        vertical_flip=True)
    data_augmented.fit(training_data)
    training_data_size = training_data.shape[0]
    aug_counter = 0
    while aug_counter < augmented_multiple - 1:
        iterator = data_augmented.flow(training_data, training_labels,
            batch_size=training_data_size)
        augmented_data = iterator.next()
        for data in augmented_data[0]:
            complete_training_data_set.append(data)
        for label in augmented_data[1]:
            complete_training_labels_set.append(label)
        aug_counter += 1
    print('Size of All Training Data: ' + str(len(complete_training_data_set)))
    print('Size of All Training Labels: ' + str(len(
        complete_training_labels_set)))
    array_training_data = np.array(complete_training_data_set)
    array_training_labels = np.array(complete_training_labels_set)
    print('Shape of complete training data: ' + str(array_training_data.shape))
    print('Shape of complete training labels: ' + str(array_training_labels
        .shape))
    return np.array(complete_training_data_set), np.array(
        complete_training_labels_set)


def savePredictedLenses(des_names_array, predicted_class_probabilities,
    predicted_lenses_filepath, text_file_path):
    """
    This saves the names of the predicted lenses in the respective textfiles.
    Args:
        des_names_array(numpy array): This is a list of the des names of the sources.
        predicted_class_probabilities(list):    This is a list of the probabilities in which lenses are predicted by
                                                the algorithm.
        predicted_lenses_filepath(string):      This is the string of the predicted lenses filepath, where this needs
                                                to be saved in the directory.
        text_file_path(string):                 This is the text file path address to which these images are saved.
    Saves:
        text_file(.txt file):                   This is the text file saved containing the predicted lenses DES names.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)
    text_file = open('%s' % text_file_path, 'a+')
    text_file.write('Predicted Lenses: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 1:
            text_file.write('%s \n ' % des_names_array[lens_index])
    text_file.write('\n')
    text_file.write('\n')
    text_file.write('No Lenses Predicted: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 0:
            text_file.write('%s \n ' % des_names_array[lens_index])
    text_file.close()


def gettingTrueFalsePositiveNegatives(testing_data, testing_labels,
    text_file_path, predicted_lenses_filepath, kf_counter=0):
    """
    This is used to get the True/False Positive and Negative values gained from the CNN confusion matrix.
    Args:
        testing_data(numpy array):          This is the unseen testing data numpy array.
        testing_labels(numpy array):        This is the unseen testing label numpy array.
        text_file_path(string):             This is the file path name of the text file in which the confusion
                                            matrix is saved.
        predicted_lenses_filepath(string):  This is the file path in which the text file is saved.
    Saves:
        This saves a confusion matrix of the True/False Positive and Negative values.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)
    predicted_data = classifier.predict_classes(testing_data)
    true_negative, false_positive, false_negative, true_positive = (
        confusion_matrix(testing_labels, predicted_data.round()).ravel())
    matrix = confusion_matrix(testing_labels, predicted_data.round())
    print(str(matrix) + ' \n ')
    print('True Positive: %s \n' % true_positive)
    print('False Negative: %s \n' % false_negative)
    print('False Positive: %s \n' % false_positive)
    print('True Negative: %s \n' % true_negative)
    text_file = open('%s' % text_file_path, 'a+')
    text_file.write('KFold Number: %s \n' % str(kf_counter))
    text_file.write('Predicted vs True Matrix: \n')
    text_file.write(str(matrix) + ' \n ')
    text_file.write('True Negative: %s \n' % str(true_negative))
    text_file.write('False Positive: %s \n' % str(false_positive))
    text_file.write('False Negative: %s \n' % str(false_negative))
    text_file.write('True Positive: %s \n' % str(true_positive))
    text_file.write('\n')
    text_file.close()
    confusion_matrix_array = [true_negative, false_positive, false_negative,
        true_positive]
    return confusion_matrix_array


def gettingKFoldConfusionMatrix(test_data, test_labels, images_47,
    labels_47, images_84, labels_84, all_unseen_images, all_unseen_labels,
    kf_counter):
    test_confusion_matrix = gettingTrueFalsePositiveNegatives(test_data,
        test_labels, text_file_path=
        '../Results/%s/TrainingTestingResults/KFold_PredictedMatrix.txt' %
        dt_string, predicted_lenses_filepath=
        '../Results/%s/TrainingTestingResults' % dt_string, kf_counter=
        kf_counter)
    confusion_matrix_47 = gettingTrueFalsePositiveNegatives(images_47,
        labels_47, text_file_path=
        '../Results/%s/Predicted47/KFold_47_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/Predicted47' %
        dt_string, kf_counter=kf_counter)
    confusion_matrix_84 = gettingTrueFalsePositiveNegatives(images_84,
        labels_84, text_file_path=
        '../Results/%s/Predicted84/KFold_84_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/Predicted84' %
        dt_string, kf_counter=kf_counter)
    all_confusion_matrix = gettingTrueFalsePositiveNegatives(all_unseen_images,
        all_unseen_labels, text_file_path=
        '../Results/%s/All_Predicted/KFold_All_LensesPredicted.txt' %
        dt_string, predicted_lenses_filepath='../Results/%s/All_Predicted' %
        dt_string, kf_counter=kf_counter)
    return (test_confusion_matrix, confusion_matrix_47, confusion_matrix_84,
        all_confusion_matrix)


def executeKFoldValidation(train_data, train_labels, val_data, val_labels,
    test_data, test_labels, images_47, labels_47, images_84, labels_84,
    all_unseen_images, all_unseen_labels):
    """
    This does the k fold cross validation which is tested against the unseen testing and known lenses.
    Args:
        train_data(numpy arrays):           This is the numpy array of the training data.
        train_labels(numpy arrays):         This is the numpy array of the training labels.
        val_data(numpy arrays):             This is the numpy array of the validation data.
        val_labels(numpy arrays):           This is the numpy array of the validation labels.
        testing_data(numpy array):          This is the numpy array of the unseen testing data.
        testing_labels(numpy array):        This is the numpy array of the unseen testing label.
        images_47(numpy array):             This is the numpy array of the unseen DES images data.
        labels_47(numpy array):             This is the numpy array of the unseen DES images labels.
        images_84(numpy array):             This is the numpy array of the unseen Jacobs images data.
        labels_84(numpy array):             This is the numpy array of the unseen Jacobs images labels.
        all_unseen_images(numpy array):     This is the numpy array of the unseen DES + Jacobs images data.
        all_unseen_labels(numpy array):     This is the numpy array of the unseen DES + Jacobs images labels.

    Saves:
        This saves the scores, mean and std. of the unseen data that is evaluated in the k fold cross validation.
    """
    if run_k_fold_validation:
        print('In executingKFoldValidation')
        kfold = StratifiedKFold(n_splits=k_fold_num, shuffle=True)
        test_scores_list = []
        unseen_47_scores_list = []
        unseen_84_scores_list = []
        all_unseen_scores_list = []
        test_matrix_list = []
        matrix_47_list = []
        matrix_84_list = []
        all_matrix_list = []
        kf_counter = 0
        for train, test in kfold.split(train_data, train_labels):
            kf_counter += 1
            print('KFold #:', kf_counter)
            model = buildClassifier()
            model.fit(train_data[train], train_labels[train], epochs=epochs,
                validation_data=(val_data, val_labels), batch_size=batch_size)
            unseen_47_scores = model.evaluate(images_47, labels_47,
                batch_size=batch_size)
            unseen_47_scores_list.append(unseen_47_scores[1] * 100)
            unseen_84_scores = model.evaluate(images_84, labels_84,
                batch_size=batch_size)
            unseen_84_scores_list.append(unseen_84_scores[1] * 100)
            test_scores = model.evaluate(test_data, test_labels, batch_size
                =batch_size)
            test_scores_list.append(test_scores[1] * 100)
            all_unseen_score = model.evaluate(all_unseen_images,
                all_unseen_labels, batch_size=batch_size)
            all_unseen_scores_list.append(all_unseen_score[1] * 100)
            (test_confusion_matrix, confusion_matrix_47,
                confusion_matrix_84, all_confusion_matrix) = (
                gettingKFoldConfusionMatrix(test_data, test_labels,
                images_47, labels_47, images_84, labels_84,
                all_unseen_images, all_unseen_labels, kf_counter))
            test_matrix_list.append(test_confusion_matrix)
            matrix_47_list.append(confusion_matrix_47)
            matrix_84_list.append(confusion_matrix_84)
            all_matrix_list.append(all_confusion_matrix)
        test_scores_mean = np.mean(test_scores_list)
        test_scores_std = np.std(test_scores_list)
        unseen_47_mean = np.mean(unseen_47_scores_list)
        unseen_47_std = np.std(unseen_47_scores_list)
        unseen_84_mean = np.mean(unseen_84_scores_list)
        unseen_84_std = np.std(unseen_84_scores_list)
        all_unseen_mean = np.mean(all_unseen_scores_list)
        all_unseen_std = np.std(all_unseen_scores_list)
        print('Test Scores: ' + str(test_scores_list))
        print('Test Scores Mean: ' + str(test_scores_mean))
        print('Test Scores Std: ' + str(test_scores_std))
        print('Unseen 47 Scores: ' + str(unseen_47_scores_list))
        print('Unseen 47 Scores Mean: ' + str(unseen_47_mean))
        print('Unseen 47 Scores Std: ' + str(unseen_47_std))
        print('Unseen 84 Scores: ' + str(unseen_84_scores_list))
        print('Unseen 84 Scores Mean: ' + str(unseen_84_mean))
        print('Unseen 84 Scores Std: ' + str(unseen_84_std))
        print('All Unseen Scores: ' + str(all_unseen_scores_list))
        print('All Unseen Scores Mean: ' + str(all_unseen_mean))
        print('All Unseen Scores Std: ' + str(all_unseen_std))
        print('Test Confusion Matrices: ' + str(test_matrix_list))
        print('47 Confusion Matrices: ' + str(matrix_47_list))
        print('84 Confusion Matrices: ' + str(matrix_84_list))
        print('All Confusion Matrices: ' + str(all_matrix_list))
        excel_headers.append('Test Scores Mean')
        excel_dictionary.append(test_scores_mean)
        excel_headers.append('Test Scores Std')
        excel_dictionary.append(test_scores_std)
        excel_headers.append('Unseen 47 Scores Mean')
        excel_dictionary.append(unseen_47_mean)
        excel_headers.append('Unseen 47 Scores Std')
        excel_dictionary.append(unseen_47_std)
        excel_headers.append('Unseen 84 Scores Mean')
        excel_dictionary.append(unseen_84_mean)
        excel_headers.append('Unseen 84 Scores Std')
        excel_dictionary.append(unseen_84_std)
        excel_headers.append('All Unseen Scores Mean')
        excel_dictionary.append(all_unseen_mean)
        excel_headers.append('All Unseen Scores Std')
        excel_dictionary.append(all_unseen_std)
        plt.plot(test_scores_list, color='red', label='Testing Scores')
        plt.plot(unseen_47_scores_list, color='blue', label='Unseen 47 Scores')
        plt.plot(unseen_84_scores_list, color='black', label='Unseen 84 Scores'
            )
        plt.plot(all_unseen_scores_list, color='green', label='Unseen Scores')
        plt.xlabel('Folds')
        plt.ylabel('Accuracy')
        plt.legend()
        plt.show()


train_pos = getPositiveImages('Training/g_r_PositiveAll', max_num,
    input_shape=input_shape)
print('Train Positive Shape: ' + str(train_pos.shape))
excel_headers.append('Train_Positive_Shape')
excel_dictionary.append(train_pos.shape)
train_neg = getNegativeImages('Training/Negative', max_num, input_shape=
    input_shape)
print('Train Negative Shape: ' + str(train_neg.shape))
excel_headers.append('Train_Negative_Shape')
excel_dictionary.append(train_neg.shape)
all_training_data, all_training_labels, _ = makeImageSet(train_pos,
    train_neg, shuffle_needed=use_shuffle)
if use_augmented_data:
    all_training_data, all_training_labels = createAugmentedData(
        all_training_data, all_training_labels)
training_data, val_data, training_labels, val_labels = train_test_split(
    all_training_data, all_training_labels, test_size=validation_split,
    shuffle=True)
excel_headers.append('All_Training_Data_Shape')
excel_dictionary.append(all_training_labels.shape)
excel_headers.append('All_Training_Labels_Shape')
excel_dictionary.append(all_training_labels.shape)
excel_headers.append('Training_Data_Shape')
excel_dictionary.append(training_data.shape)
excel_headers.append('Validation_Data_Shape')
excel_dictionary.append(val_data.shape)
excel_headers.append('Training_Labels_Shape')
excel_dictionary.append(training_labels.shape)
excel_headers.append('Validation_Labels_Shape')
excel_dictionary.append(val_labels.shape)
excel_headers.append('Validation_Split')
excel_dictionary.append(validation_split)
history, classifier = usingCnnModel(training_data, training_labels,
    val_data, val_labels)
classifier.load_weights('best_weights.hdf5')
classifier.save_weights('galaxies_cnn.h5')
excel_headers.append('Epochs')
excel_dictionary.append(epochs)
excel_headers.append('Batch_size')
excel_dictionary.append(batch_size)
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
number_of_completed_epochs = range(1, len(acc) + 1)
train_val_accuracy_figure = plt.figure()
plt.plot(number_of_completed_epochs, acc, label='Training acc')
plt.plot(number_of_completed_epochs, val_acc, label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
train_val_accuracy_figure.savefig(
    '../Results/g_r_%s/TrainingValidationAccuracy.png' % dt_string)
plt.close()
train_val_loss_figure = plt.figure()
plt.plot(number_of_completed_epochs, loss, label='Training loss')
plt.plot(number_of_completed_epochs, val_loss, label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.xlabel('Epochs')
plt.ylabel('Loss')
train_val_loss_figure.savefig(
    '../Results/g_r_%s/TrainingValidationLoss.png' % dt_string)
plt.close()
if not os.path.exists('../Results/g_r_%s/PositiveResults/' % dt_string):
    os.mkdir('../Results/g_r_%s/PositiveResults/' % dt_string)
if not os.path.exists('../Results/g_r_%s/NegativeResults/' % dt_string):
    os.mkdir('../Results/g_r_%s/NegativeResults/' % dt_string)
img_positive_tensor = getPositiveImages('Training/g_r_PositiveAll', 1,
    input_shape=input_shape)
positive_train_figure = plt.figure()
plt.imshow(img_positive_tensor[0])
print(img_positive_tensor.shape)
positive_train_figure.savefig(
    '../Results/g_r_%s/PositiveResults/PositiveTrainingFigure.png' % dt_string)
plt.close()
visualiseActivations(img_positive_tensor, base_dir=
    '../Results/g_r_%s/PositiveResults/' % dt_string)
img_negative_tensor = getNegativeImages('Training/Negative', 1, input_shape
    =input_shape)
negative_train_figure = plt.figure()
plt.imshow(img_negative_tensor[0])
print(img_negative_tensor.shape)
negative_train_figure.savefig(
    '../Results/g_r_%s/NegativeResults/NegativeTrainingFigure.png' % dt_string)
plt.close()
visualiseActivations(img_negative_tensor, base_dir=
    '../Results/g_r_%s/NegativeResults/' % dt_string)
test_pos = getPositiveImages('Testing/g_r_PositiveAll', max_num_testing,
    input_shape)
test_neg = getNegativeImages('Testing/Negative', max_num_testing, input_shape)
testing_data, testing_labels, _ = makeImageSet(test_pos, test_neg,
    shuffle_needed=True)
print('Testing Data Shape:  ' + str(testing_data.shape))
print('Testing Labels Shape: ' + str(testing_labels.shape))
print('Got Unseen Testing data')
scores = classifier.evaluate(testing_data, testing_labels, batch_size=
    batch_size)
print('Test loss: %s' % scores[0])
print('Test accuracy: %s' % scores[1])
excel_headers.append('Test_Loss')
excel_dictionary.append(scores[0])
excel_headers.append('Test_Accuracy')
excel_dictionary.append(scores[1])
gettingTrueFalsePositiveNegatives(testing_data, testing_labels,
    text_file_path=
    '../Results/g_r_%s/TrainingTestingResults/ActualPredictedMatrix.txt' %
    dt_string, predicted_lenses_filepath=
    '../Results/g_r_%s/TrainingTestingResults' % dt_string)
known_47_images = getUnseenData('UnseenData/Known47', max_num_prediction,
    input_shape=input_shape)
images_47, labels_47, des_47_names = makeImageSet(positive_images=list(
    known_47_images.values()), known_des_names=list(known_47_images.keys()),
    shuffle_needed=True)
print('47 Images Shape:  ' + str(images_47.shape))
print('47 Labels Shape: ' + str(labels_47.shape))
print('Got Unseen 47 data')
predicted_class_probabilities_47 = classifier.predict_classes(images_47,
    batch_size=batch_size)
lens_predicted_count_47 = np.count_nonzero(predicted_class_probabilities_47 ==
    1)
non_lens_predicted_count_47 = np.count_nonzero(
    predicted_class_probabilities_47 == 0)
print('%s/47 known images predicted' % lens_predicted_count_47)
print('%s/47 non lensed images predicted' % non_lens_predicted_count_47)
gettingTrueFalsePositiveNegatives(images_47, labels_47, text_file_path=
    '../Results/g_r_%s/Predicted47/47_LensesPredicted.txt' % dt_string,
    predicted_lenses_filepath='../Results/g_r_%s/Predicted47' % dt_string)
savePredictedLenses(des_47_names, predicted_class_probabilities_47,
    predicted_lenses_filepath='../Results/g_r_%s/Predicted47' % dt_string,
    text_file_path='../Results/g_r_%s/Predicted47/47_LensesPredicted.txt' %
    dt_string)
excel_headers.append('Predicted_Lens_47')
excel_dictionary.append(lens_predicted_count_47)
excel_headers.append('Predicted_No_Lens_47')
excel_dictionary.append(non_lens_predicted_count_47)
known_84_images = getUnseenData('UnseenData/Known84', max_num_prediction,
    input_shape=input_shape)
images_84, labels_84, des_84_names = makeImageSet(positive_images=list(
    known_84_images.values()), known_des_names=list(known_84_images.keys()),
    shuffle_needed=True)
print('84 Images Shape:  ' + str(images_84.shape))
print('84 Labels Shape: ' + str(labels_84.shape))
print('Got Unseen 84 data')
predicted_class_probabilities_84 = classifier.predict_classes(images_84,
    batch_size=batch_size)
lens_predicted_count_84 = np.count_nonzero(predicted_class_probabilities_84 ==
    1)
non_lens_predicted_count_84 = np.count_nonzero(
    predicted_class_probabilities_84 == 0)
print('%s/84 known images predicted' % lens_predicted_count_84)
print('%s/84 non lensed images predicted' % non_lens_predicted_count_84)
gettingTrueFalsePositiveNegatives(images_84, labels_84, text_file_path=
    '../Results/g_r_%s/Predicted84/84_LensesPredicted.txt' % dt_string,
    predicted_lenses_filepath='../Results/g_r_%s/Predicted84' % dt_string)
savePredictedLenses(des_84_names, predicted_class_probabilities_84,
    predicted_lenses_filepath='../Results/g_r_%s/Predicted84' % dt_string,
    text_file_path='../Results/g_r_%s/Predicted84/84_LensesPredicted.txt' %
    dt_string)
excel_headers.append('Predicted_Lens_84')
excel_dictionary.append(lens_predicted_count_84)
excel_headers.append('Predicted_No_Lens_84')
excel_dictionary.append(non_lens_predicted_count_84)
all_unseen_images = np.concatenate((images_47, images_84))
all_unseen_labels = np.concatenate((labels_47, labels_84))
all_des_names = np.concatenate((des_47_names, des_84_names))
print('All Data Shape: ' + str(all_unseen_images.shape))
print('All Data Labels: ' + str(all_unseen_labels.shape))
all_predicted_class_probabilities = classifier.predict_classes(
    all_unseen_images, batch_size=batch_size)
all_lens_predicted_count = np.count_nonzero(
    all_predicted_class_probabilities == 1)
all_non_lens_predicted_count = np.count_nonzero(
    all_predicted_class_probabilities == 0)
print('%s/131 known images predicted' % all_lens_predicted_count)
print('%s/131 non lensed images predicted' % all_non_lens_predicted_count)
gettingTrueFalsePositiveNegatives(all_unseen_images, all_unseen_labels,
    text_file_path='../Results/g_r_%s/PredictedAll/All_LensesPredicted.txt' %
    dt_string, predicted_lenses_filepath='../Results/g_r_%s/PredictedAll' %
    dt_string)
savePredictedLenses(all_des_names, all_predicted_class_probabilities,
    predicted_lenses_filepath='../Results/g_r_%s/PredictedAll' % dt_string,
    text_file_path='../Results/g_r_%s/Predicted84/All_LensesPredicted.txt' %
    dt_string)
excel_headers.append('Predicted_Lens_All')
excel_dictionary.append(all_lens_predicted_count)
excel_headers.append('Predicted_No_Lens_All')
excel_dictionary.append(all_non_lens_predicted_count)
executeKFoldValidation(training_data, training_labels, val_data, val_labels,
    testing_data, testing_labels, images_47, labels_47, images_84,
    labels_84, all_unseen_images, all_unseen_labels)
print('Test loss of normal CNN: %s' % scores[0])
print('Test accuracy of normal CNN: %s' % (scores[1] * 100))
if makeNewCSVFile:
    createExcelSheet('../Results/new_kerasCNN_Results.csv', excel_headers)
    writeToFile('../Results/new_kerasCNN_Results.csv', excel_dictionary)
else:
    writeToFile('../Results/new_kerasCNN_Results.csv', excel_dictionary)

"""
This is file performs the convolutional neural network algorithm, in which the k fold is performed as well.
The results were saved in a csv file.
"""

import os
import sys
from datetime import datetime

import numpy as np
from astropy.io import fits
from astropy.utils.data import get_pkg_data_filename
from matplotlib import pyplot as plt
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split, StratifiedKFold
from sklearn.utils import shuffle
from tensorflow.python.keras import Sequential
from tensorflow.python.keras.callbacks import ModelCheckpoint, EarlyStopping
from tensorflow.python.keras.layers.convolutional import Conv2D, MaxPooling2D
from tensorflow.python.keras.layers.core import Dense, Dropout, Flatten
from tensorflow.python.keras.models import Model
from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from tensorflow.python.keras.utils.vis_utils import plot_model
from ExcelUtils import createExcelSheet, writeToFile
import tensorflow
print(tensorflow.__version__)

now = datetime.now()
dt_string = now.strftime("%d_%m_%Y_%H_%M_%S")
print(dt_string)
excel_headers = []
excel_dictionary = []
excel_headers.append("Date and Time")
excel_dictionary.append(dt_string)

# Globals
makeNewCSVFile = True
max_num = sys.maxsize  # Set to sys.maxsize when running entire data set
max_num_testing = sys.maxsize  # Set to sys.maxsize when running entire data set
max_num_prediction = sys.maxsize  # Set to sys.maxsize when running entire data set
validation_split = 0.2  # A float value between 0 and 1 that determines what percentage of the training
# data is used for validation.
k_fold_num = 5  # A number between 1 and 10 that determines how many times the k-fold classifier
# is trained.
epochs = 5  # A number that dictates how many iterations should be run to train the classifier
batch_size = 128  # The number of items batched together during training.
run_k_fold_validation = True  # Set this to True if you want to run K-Fold validation as well.
input_shape = (100, 100, 3)  # The shape of the images being learned & evaluated.
augmented_multiple = 2  # This uses data augmentation to generate x-many times as much data as there is on file.
use_augmented_data = True  # Determines whether to use data augmentation or not.
patience_num = 10  # Used in the early stopping to determine how quick/slow to react.
use_early_stopping = True  # Determines whether to use early stopping or not.
use_model_checkpoint = True  # Determines whether the classifiers keeps track of the most accurate iteration of itself.
monitor_early_stopping = 'val_loss'
monitor_model_checkpoint = 'val_acc'
use_shuffle = True

# Adding global parameters to excel
excel_headers.append("Max Training Num")
excel_dictionary.append(max_num)
excel_headers.append("Max Testing Num")
excel_dictionary.append(max_num_testing)
excel_headers.append("Max Prediction Num")
excel_dictionary.append(max_num_prediction)
excel_headers.append("Validation Split")
excel_dictionary.append(validation_split)
excel_headers.append("K fold Num")
excel_dictionary.append(k_fold_num)
excel_headers.append("Epochs")
excel_dictionary.append(epochs)
excel_headers.append("Batch Size")
excel_dictionary.append(batch_size)
excel_headers.append("Run K fold")
excel_dictionary.append(run_k_fold_validation)
excel_headers.append("Input Shape")
excel_dictionary.append(input_shape)
excel_headers.append("Augmented Multiple")
excel_dictionary.append(augmented_multiple)
excel_headers.append("Use Augmented Data")
excel_dictionary.append(use_augmented_data)
excel_headers.append("Patience")
excel_dictionary.append(patience_num)
excel_headers.append("Use Early Stopping")
excel_dictionary.append(use_early_stopping)
excel_headers.append("Use Model Checkpoint")
excel_dictionary.append(use_model_checkpoint)
excel_headers.append("Monitor Early Stopping")
excel_dictionary.append(monitor_early_stopping)
excel_headers.append("Monitor Model Checkpoint")
excel_dictionary.append(monitor_model_checkpoint)

if not os.path.exists('../Results/g_r_%s/' % dt_string):
    os.mkdir('../Results/g_r_%s/' % dt_string)


# Helper methods
def getPositiveImages(images_dir, max_num, input_shape):
    """
    This gets the positively simulated images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the positively simulated images.
        max_num(integer):   This is the number of sources of the positively simulated images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        positive_images(numpy array):   This is the numpy array of the positively simulated images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        positive_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/%s_g_norm.fits' % (os.path.join(root, folder), folder))
            r_img_path = get_pkg_data_filename('%s/%s_r_norm.fits' % (os.path.join(root, folder), folder))
            i_img_path = get_pkg_data_filename('%s/%s_i_norm.fits' % (os.path.join(root, folder), folder))

            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]

            img_data = [g_data, r_data, i_data]
            positive_images[index] = img_data
            index += 1

            if index >= num_of_images:
                break
        return positive_images.reshape(num_of_images, input_shape[0], input_shape[1], input_shape[2])


def getNegativeImages(images_dir, max_num, input_shape):
    """
    This gets the negative images in the g, r and  i bands.
    Args:
        images_dir(string): This is the file path address of the negative images.
        max_num(integer):   This is the number of sources of the negative images to be used.
        input_shape(tuple): This is the shape of the images.
    Returns:
        negative_images(numpy array):   This is the numpy array of the negative images with the shape of
                                        (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                        (num_of_images, 100, 100, 3).
    """
    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        negative_images = np.zeros([num_of_images, 3, 100, 100])
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % (os.path.join(root, folder)))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % (os.path.join(root, folder)))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % (os.path.join(root, folder)))

            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]

            img_data = [g_data, r_data, i_data]
            negative_images[index] = img_data
            index += 1

            if index >= num_of_images:
                break
        return negative_images.reshape(num_of_images, input_shape[0], input_shape[1], input_shape[2])


def getUnseenData(images_dir, max_num, input_shape):
    """
        This gets the unseen images in the g, r and  i bands containing the identified known lenses.
        Args:
            images_dir(string): This is the file path address of the unseen images.
            max_num(integer):   This is the number of sources of the unseen images to be used.
            input_shape(tuple): This is the shape of the images.
        Returns:
            des_tiles(dictionary):   This is the dictionary of the unseen images with the shape of
                                            (num of images, input_shape[0], input_shape[1], input_shape[2]) =
                                            (num_of_images, 100, 100, 3).
        """

    des_tiles = {}

    for root, dirs, _ in os.walk(images_dir):
        num_of_images = min(max_num, len(dirs))
        index = 0
        for folder in dirs:
            g_img_path = get_pkg_data_filename('%s/g_norm.fits' % (os.path.join(root, folder)))
            r_img_path = get_pkg_data_filename('%s/r_norm.fits' % (os.path.join(root, folder)))
            i_img_path = get_pkg_data_filename('%s/i_norm.fits' % (os.path.join(root, folder)))

            g_data = fits.open(g_img_path)[0].data[0:100, 0:100]
            r_data = fits.open(r_img_path)[0].data[0:100, 0:100]
            i_data = fits.open(i_img_path)[0].data[0:100, 0:100]

            img_data = np.array([g_data, r_data, i_data]).reshape(input_shape[0], input_shape[1], input_shape[2])
            des_tiles.update({folder: img_data})
            index += 1
            if index >= num_of_images:
                break

        return des_tiles


def makeImageSet(positive_images, negative_images=None, known_des_names=None, neg_des_names=None,
                 shuffle_needed=use_shuffle):
    """
    This is used to create data set of images and labels, in which the positive and negative images are all
    combined and shuffled.
    Args:
        positive_images(numpy array):   This is the numpy array of the positively simulated images.
        negative_images(numpy array):   This is the numpy array of the negative images, this is set to a
                                        default of None.
        known_des_names(list):    This is the dictionary of the unseen known lenses, this is set to a
                                        default of None.
        neg_des_names(list):      This is the dictionary of the negative images, this is set to a
                                        default of None.
        shuffle_needed(boolean):        This is a boolean value to determine whether or not shuffling of the given data
                                        sets is required.
    Returns:
        image_set(numpy array):         This is the image data set of  numpy array of the combination positive
                                        and negative images.
        label_set(numpy array):         This is the label data set of  numpy array of the combination positive
                                        and negative label.
        des_names_set(numpy array):     This is the des name data set of the known lenses and negative images used.
    """
    # if negative_images is None:
    #     negative_images = []
    #     # known_des_names = {}
    #     neg_des_names = {}

    image_set = []
    label_set = []
    des_names_set = []

    # If there is none in objects for the known_des_names and neg_des_names
    if known_des_names is None and neg_des_names is None:
        for index_none in range(0, len(positive_images)):
            image_set.append(positive_images[index_none])
            label_set.append(1)

        for index_none in range(0, len(negative_images)):
            image_set.append(negative_images[index_none])
            label_set.append(0)

        if shuffle_needed:
            image_set, label_set = shuffle(image_set, label_set)

    else:  # if there is names for des
        for index_des in range(0, len(positive_images)):
            image_set.append(positive_images[index_des])
            label_set.append(1)
            des_names_set.append(known_des_names[index_des])

        for index_des in range(0, len(negative_images)):
            image_set.append(negative_images[index_des])
            label_set.append(0)
            des_names_set.append(neg_des_names[index_des])

        if shuffle_needed:
            image_set, label_set, des_names_set = shuffle(image_set, label_set, des_names_set)

    return np.array(image_set), np.array(label_set), np.array(des_names_set)


def buildClassifier(input_shape=(100, 100, 3)):
    """
    This creates the CNN algorithm.
    Args:
        input_shape(tuple): This is the image shape of (100,100,3)
    Returns:
        classifier(sequential): This is the sequential model.
    """
    # Initialising the CNN
    classifier = Sequential()
    classifier.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape, padding='same'))
    classifier.add(MaxPooling2D(pool_size=(3, 3), padding='same'))
    classifier.add(Dropout(0.5))  # added extra Dropout layer
    classifier.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Conv2D(128, (3, 3), padding='same', activation='relu'))
    classifier.add(Dropout(0.5))  # added extra dropout layer
    classifier.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dropout(0.2))  # antes era 0.25
    classifier.add(Conv2D(512, (3, 3), padding='same', activation='relu'))
    classifier.add(Conv2D(1024, (3, 3), activation='relu', padding='same'))
    classifier.add(MaxPooling2D(pool_size=(2, 2), padding='same'))
    classifier.add(Dense(units=1024, activation='relu'))  # added new dense layer
    classifier.add(Dropout(0.2))  # antes era 0.25
    # Step 3 - Flattening
    classifier.add(Flatten())
    classifier.add(Dense(units=1024, activation='relu'))  # added new dense layer
    classifier.add(Dense(units=256, activation='relu'))  # added new dense layer
    # Step 4 - Full connection
    classifier.add(Dropout(0.2))
    classifier.add(Dense(units=1, activation='sigmoid'))
    classifier.summary()

    # Compiling the CNN
    classifier.compile(optimizer='adam',
                       loss='binary_crossentropy',
                       metrics=['accuracy'])
    plot_model(classifier, to_file='model_plot.png', show_shapes=True, show_layer_names=True)
    return classifier


def visualiseActivations(img_tensor, base_dir):
    """
    This makes images of the activations, as the selected image passed through the model
    Args:
        img_tensor(numpy array):    This is the numpy array of the selected image
        base_dir(string):           This is the file path name
    Saves:
        This saves the activation images of the selected source.
    """
    global predicted_class, size
    # Run prediction on that image
    predicted_class = classifier.predict_classes(img_tensor, batch_size=10)
    print("Predicted class is: ", predicted_class)
    # Visualize activations
    layer_outputs = [layer.output for layer in classifier.layers[:12]]
    activation_model = Model(inputs=classifier.input, outputs=layer_outputs)
    activations = activation_model.predict(img_tensor)
    layer_names = []
    for layer in classifier.layers[:12]:
        layer_names.append(layer.name)
    images_per_row = 3
    count = 0
    for layer_name, layer_activation in zip(layer_names, activations):
        number_of_features = layer_activation.shape[-1]
        size = layer_activation.shape[1]
        number_of_columns = number_of_features // images_per_row
        display_grid = np.zeros((size * number_of_columns, images_per_row * size))
        for col in range(number_of_columns):
            for row in range(images_per_row):
                channel_image = layer_activation[0, :, :, col * images_per_row + row]
                channel_image -= channel_image.mean()
                channel_image /= channel_image.std()
                channel_image *= 64
                channel_image += 128
                channel_image = np.clip(channel_image, 0, 255).astype('uint8')
                display_grid[col * size: (col + 1) * size, row * size: (row + 1) * size] = channel_image
        scale = 1. / size
        activations_figure = plt.figure(figsize=(scale * display_grid.shape[1],
                                                 scale * display_grid.shape[0]))
        plt.title(layer_name)
        plt.grid(False)
        plt.imshow(display_grid, aspect='auto', cmap='viridis')
        activations_figure.savefig('%s/%s_Activation_%s.png' % (base_dir, count, layer_name))
        plt.close()

        count += 1


def usingCnnModel(training_data, training_labels, val_data, val_labels):
    """
    This is using the CNN model and setting it up.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
        val_data(numpy arrays):         This is the numpy array of the validation data.
        val_labels(numpy arrays):       This is the numpy array of the validation labels.
    Returns:
        history(history):               This is the history of the classifier.
        classifier(sequential):         This is the cnn model classifier fitted to the training data and labels.
    """
    model_checkpoint = ModelCheckpoint(filepath="best_weights.hdf5",
                                       monitor=monitor_model_checkpoint,
                                       save_best_only=True)

    early_stopping = EarlyStopping(monitor=monitor_early_stopping, patience=patience_num)  # original patience =3

    classifier = buildClassifier()
    callbacks_array = []
    if use_early_stopping:
        callbacks_array.append(early_stopping)
    if use_model_checkpoint:
        callbacks_array.append(model_checkpoint)

    print(len(training_data))
    history = classifier.fit(training_data,
                             training_labels,
                             epochs=epochs,
                             validation_data=(val_data, val_labels),
                             callbacks=callbacks_array,
                             batch_size=batch_size
                             # steps_per_epoch=int(len(training_data) / batch_size),
                             )
    return history, classifier


def createAugmentedData(training_data, training_labels):
    """
    This is creates the augmented data.
    Args:
        training_data(numpy arrays):    This is the numpy array of the training data.
        training_labels(numpy arrays):  This is the numpy array of the training labels.
    Returns:
        complete_training_data_set(numpy array):    This is the numpy array of the total training data, which is has
                                                    undergone augmentation.
        complete_training_labels_set(numpy array):  This is the numpy array of the total training labels, which is has
                                                    undergone augmentation.
    """
    complete_training_data_set = []
    complete_training_labels_set = []

    for data in training_data:
        complete_training_data_set.append(data)
    print("Complete Training Data: " + str(len(complete_training_data_set)))

    for label in training_labels:
        complete_training_labels_set.append(label)
    print("Complete Training Label: " + str(len(complete_training_labels_set)))

    # create augmented data
    data_augmented = ImageDataGenerator(featurewise_center=True,
                                        featurewise_std_normalization=True,
                                        rotation_range=90,
                                        width_shift_range=0.2,
                                        height_shift_range=0.2,
                                        horizontal_flip=True,
                                        vertical_flip=True)
    data_augmented.fit(training_data)

    training_data_size = training_data.shape[0]
    aug_counter = 0
    while aug_counter < (augmented_multiple - 1):
        iterator = data_augmented.flow(training_data, training_labels, batch_size=training_data_size)
        # iterator = data_augmented.flow(training_data, training_labels, batch_size=batch_size)
        augmented_data = iterator.next()
        for data in augmented_data[0]:
            complete_training_data_set.append(data)
        for label in augmented_data[1]:
            complete_training_labels_set.append(label)
        aug_counter += 1

    print("Size of All Training Data: " + str(len(complete_training_data_set)))
    print("Size of All Training Labels: " + str(len(complete_training_labels_set)))

    array_training_data = np.array(complete_training_data_set)
    array_training_labels = np.array(complete_training_labels_set)

    print("Shape of complete training data: " + str(array_training_data.shape))
    print("Shape of complete training labels: " + str(array_training_labels.shape))

    return np.array(complete_training_data_set), np.array(complete_training_labels_set)


def savePredictedLenses(des_names_array, predicted_class_probabilities, predicted_lenses_filepath, text_file_path):
    """
    This saves the names of the predicted lenses in the respective textfiles.
    Args:
        des_names_array(numpy array): This is a list of the des names of the sources.
        predicted_class_probabilities(list):    This is a list of the probabilities in which lenses are predicted by
                                                the algorithm.
        predicted_lenses_filepath(string):      This is the string of the predicted lenses filepath, where this needs
                                                to be saved in the directory.
        text_file_path(string):                 This is the text file path address to which these images are saved.
    Saves:
        text_file(.txt file):                   This is the text file saved containing the predicted lenses DES names.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)
    text_file = open('%s' % text_file_path, "a+")
    text_file.write('Predicted Lenses: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 1:
            text_file.write("%s \n " % des_names_array[lens_index])

    text_file.write('\n')
    text_file.write('\n')

    text_file.write('No Lenses Predicted: \n')
    for lens_index in range(len(predicted_class_probabilities)):
        if predicted_class_probabilities[lens_index] == 0:
            text_file.write("%s \n " % des_names_array[lens_index])
    text_file.close()


def gettingTrueFalsePositiveNegatives(testing_data, testing_labels, text_file_path,
                                      predicted_lenses_filepath, kf_counter=0):
    """
    This is used to get the True/False Positive and Negative values gained from the CNN confusion matrix.
    Args:
        testing_data(numpy array):          This is the unseen testing data numpy array.
        testing_labels(numpy array):        This is the unseen testing label numpy array.
        text_file_path(string):             This is the file path name of the text file in which the confusion
                                            matrix is saved.
        predicted_lenses_filepath(string):  This is the file path in which the text file is saved.
    Saves:
        This saves a confusion matrix of the True/False Positive and Negative values.
    """
    if not os.path.exists(predicted_lenses_filepath):
        os.mkdir('%s/' % predicted_lenses_filepath)

    predicted_data = classifier.predict_classes(testing_data)
    true_negative, false_positive, false_negative, true_positive = confusion_matrix(testing_labels,
                                                                                    predicted_data.round()).ravel()
    matrix = (confusion_matrix(testing_labels, predicted_data.round()))
    print(str(matrix) + ' \n ')
    print("True Positive: %s \n" % true_positive)
    print("False Negative: %s \n" % false_negative)
    print("False Positive: %s \n" % false_positive)
    print("True Negative: %s \n" % true_negative)

    text_file = open('%s' % text_file_path, "a+")
    text_file.write('KFold Number: %s \n' % str(kf_counter))
    text_file.write('Predicted vs True Matrix: \n')
    text_file.write(str(matrix) + " \n ")
    text_file.write("True Negative: %s \n" % str(true_negative))
    text_file.write("False Positive: %s \n" % str(false_positive))
    text_file.write("False Negative: %s \n" % str(false_negative))
    text_file.write("True Positive: %s \n" % str(true_positive))
    text_file.write("\n")
    text_file.close()

    confusion_matrix_array = [true_negative, false_positive, false_negative, true_positive]
    return confusion_matrix_array


def gettingKFoldConfusionMatrix(test_data, test_labels, images_47, labels_47, images_84, labels_84, all_unseen_images,
                                all_unseen_labels, kf_counter):
    test_confusion_matrix = gettingTrueFalsePositiveNegatives(test_data,
                                                              test_labels,
                                                              text_file_path='../Results/%s/TrainingTestingResults'
                                                                             '/KFold_PredictedMatrix.txt' % dt_string,
                                                              predicted_lenses_filepath='../Results/%s/TrainingTestingResults'
                                                                                        % dt_string,
                                                              kf_counter=kf_counter)

    confusion_matrix_47 = gettingTrueFalsePositiveNegatives(images_47,
                                                            labels_47,
                                                            text_file_path='../Results/%s/Predicted47'
                                                                           '/KFold_47_LensesPredicted.txt' %
                                                                           dt_string,
                                                            predicted_lenses_filepath='../Results/%s/Predicted47'
                                                                                      % dt_string,
                                                            kf_counter=kf_counter)

    confusion_matrix_84 = gettingTrueFalsePositiveNegatives(images_84,
                                                            labels_84,
                                                            text_file_path='../Results/%s/Predicted84'
                                                                           '/KFold_84_LensesPredicted.txt' %
                                                                           dt_string,
                                                            predicted_lenses_filepath='../Results/%s/Predicted84'
                                                                                      % dt_string,
                                                            kf_counter=kf_counter)

    all_confusion_matrix = gettingTrueFalsePositiveNegatives(all_unseen_images,
                                                             all_unseen_labels,
                                                             text_file_path='../Results/%s/All_Predicted'
                                                                            '/KFold_All_LensesPredicted.txt' % dt_string,
                                                             predicted_lenses_filepath='../Results/%s'
                                                                                       '/All_Predicted' %
                                                                                       dt_string,
                                                             kf_counter = kf_counter)

    return test_confusion_matrix, confusion_matrix_47, confusion_matrix_84, all_confusion_matrix


def executeKFoldValidation(train_data, train_labels, val_data, val_labels, test_data, test_labels,
                           images_47, labels_47, images_84, labels_84, all_unseen_images, all_unseen_labels):
    """
    This does the k fold cross validation which is tested against the unseen testing and known lenses.
    Args:
        train_data(numpy arrays):           This is the numpy array of the training data.
        train_labels(numpy arrays):         This is the numpy array of the training labels.
        val_data(numpy arrays):             This is the numpy array of the validation data.
        val_labels(numpy arrays):           This is the numpy array of the validation labels.
        testing_data(numpy array):          This is the numpy array of the unseen testing data.
        testing_labels(numpy array):        This is the numpy array of the unseen testing label.
        images_47(numpy array):             This is the numpy array of the unseen DES images data.
        labels_47(numpy array):             This is the numpy array of the unseen DES images labels.
        images_84(numpy array):             This is the numpy array of the unseen Jacobs images data.
        labels_84(numpy array):             This is the numpy array of the unseen Jacobs images labels.
        all_unseen_images(numpy array):     This is the numpy array of the unseen DES + Jacobs images data.
        all_unseen_labels(numpy array):     This is the numpy array of the unseen DES + Jacobs images labels.

    Saves:
        This saves the scores, mean and std. of the unseen data that is evaluated in the k fold cross validation.
    """
    if run_k_fold_validation:
        print("In executingKFoldValidation")

        # this is doing it manually:
        kfold = StratifiedKFold(n_splits=k_fold_num, shuffle=True)

        test_scores_list = []
        unseen_47_scores_list = []
        unseen_84_scores_list = []
        all_unseen_scores_list = []

        test_matrix_list = []
        matrix_47_list = []
        matrix_84_list = []
        all_matrix_list = []
        kf_counter = 0

        for train, test in kfold.split(train_data, train_labels):
            kf_counter += 1
            print('KFold #:', kf_counter)

            model = buildClassifier()
            # fit the model
            model.fit(train_data[train],
                      train_labels[train],
                      epochs=epochs,
                      validation_data=(val_data, val_labels),
                      batch_size=batch_size
                      )

            unseen_47_scores = model.evaluate(images_47, labels_47, batch_size=batch_size)
            unseen_47_scores_list.append(unseen_47_scores[1] * 100)
            unseen_84_scores = model.evaluate(images_84, labels_84, batch_size=batch_size)
            unseen_84_scores_list.append(unseen_84_scores[1] * 100)
            test_scores = model.evaluate(test_data, test_labels, batch_size=batch_size)
            test_scores_list.append(test_scores[1] * 100)
            all_unseen_score = model.evaluate(all_unseen_images, all_unseen_labels, batch_size=batch_size)
            all_unseen_scores_list.append(all_unseen_score[1] * 100)

            # show confusion matrix
            test_confusion_matrix, confusion_matrix_47, confusion_matrix_84, all_confusion_matrix = \
                gettingKFoldConfusionMatrix(test_data, test_labels, images_47, labels_47, images_84, labels_84,
                                            all_unseen_images,
                                            all_unseen_labels, kf_counter)
            test_matrix_list.append(test_confusion_matrix)
            matrix_47_list.append(confusion_matrix_47)
            matrix_84_list.append(confusion_matrix_84)
            all_matrix_list.append(all_confusion_matrix)

        test_scores_mean = np.mean(test_scores_list)
        test_scores_std = np.std(test_scores_list)
        unseen_47_mean = np.mean(unseen_47_scores_list)
        unseen_47_std = np.std(unseen_47_scores_list)
        unseen_84_mean = np.mean(unseen_84_scores_list)
        unseen_84_std = np.std(unseen_84_scores_list)
        all_unseen_mean = np.mean(all_unseen_scores_list)
        all_unseen_std = np.std(all_unseen_scores_list)

        print("Test Scores: " + str(test_scores_list))
        print("Test Scores Mean: " + str(test_scores_mean))
        print("Test Scores Std: " + str(test_scores_std))
        print("Unseen 47 Scores: " + str(unseen_47_scores_list))
        print("Unseen 47 Scores Mean: " + str(unseen_47_mean))
        print("Unseen 47 Scores Std: " + str(unseen_47_std))
        print("Unseen 84 Scores: " + str(unseen_84_scores_list))
        print("Unseen 84 Scores Mean: " + str(unseen_84_mean))
        print("Unseen 84 Scores Std: " + str(unseen_84_std))
        print("All Unseen Scores: " + str(all_unseen_scores_list))
        print("All Unseen Scores Mean: " + str(all_unseen_mean))
        print("All Unseen Scores Std: " + str(all_unseen_std))
        print("Test Confusion Matrices: " + str(test_matrix_list))
        print("47 Confusion Matrices: " + str(matrix_47_list))
        print("84 Confusion Matrices: " + str(matrix_84_list))
        print("All Confusion Matrices: " + str(all_matrix_list))

        excel_headers.append("Test Scores Mean")
        excel_dictionary.append(test_scores_mean)
        excel_headers.append("Test Scores Std")
        excel_dictionary.append(test_scores_std)
        excel_headers.append("Unseen 47 Scores Mean")
        excel_dictionary.append(unseen_47_mean)
        excel_headers.append("Unseen 47 Scores Std")
        excel_dictionary.append(unseen_47_std)
        excel_headers.append("Unseen 84 Scores Mean")
        excel_dictionary.append(unseen_84_mean)
        excel_headers.append("Unseen 84 Scores Std")
        excel_dictionary.append(unseen_84_std)
        excel_headers.append("All Unseen Scores Mean")
        excel_dictionary.append(all_unseen_mean)
        excel_headers.append("All Unseen Scores Std")
        excel_dictionary.append(all_unseen_std)

        plt.plot(test_scores_list, color='red', label='Testing Scores')
        plt.plot(unseen_47_scores_list, color='blue', label='Unseen 47 Scores')
        plt.plot(unseen_84_scores_list, color='black', label='Unseen 84 Scores')
        plt.plot(all_unseen_scores_list, color='green', label='Unseen Scores')
        plt.xlabel('Folds')
        plt.ylabel('Accuracy')
        plt.legend()

        plt.show()


# __________________________________________________________________________
# MAIN

# Get positive training data
train_pos = getPositiveImages('Training/g_r_PositiveAll', max_num, input_shape=input_shape)
print("Train Positive Shape: " + str(train_pos.shape))
excel_headers.append("Train_Positive_Shape")
excel_dictionary.append(train_pos.shape)

# Get negative training data
train_neg = getNegativeImages('Training/Negative', max_num, input_shape=input_shape)
print("Train Negative Shape: " + str(train_neg.shape))
excel_headers.append("Train_Negative_Shape")
excel_dictionary.append(train_neg.shape)

all_training_data, all_training_labels, _ = makeImageSet(train_pos, train_neg, shuffle_needed=use_shuffle)
if use_augmented_data:
    all_training_data, all_training_labels = createAugmentedData(all_training_data, all_training_labels)

training_data, val_data, training_labels, val_labels = train_test_split(all_training_data,
                                                                        all_training_labels,
                                                                        test_size=validation_split,
                                                                        shuffle=True)
excel_headers.append("All_Training_Data_Shape")
excel_dictionary.append(all_training_labels.shape)
excel_headers.append("All_Training_Labels_Shape")
excel_dictionary.append(all_training_labels.shape)
excel_headers.append("Training_Data_Shape")
excel_dictionary.append(training_data.shape)
excel_headers.append("Validation_Data_Shape")
excel_dictionary.append(val_data.shape)
excel_headers.append("Training_Labels_Shape")
excel_dictionary.append(training_labels.shape)
excel_headers.append("Validation_Labels_Shape")
excel_dictionary.append(val_labels.shape)
excel_headers.append("Validation_Split")
excel_dictionary.append(validation_split)

history, classifier = usingCnnModel(training_data,
                                    training_labels,
                                    val_data,
                                    val_labels)

classifier.load_weights('best_weights.hdf5')
classifier.save_weights('galaxies_cnn.h5')

excel_headers.append("Epochs")
excel_dictionary.append(epochs)
excel_headers.append("Batch_size")
excel_dictionary.append(batch_size)

# Plot run metrics
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
number_of_completed_epochs = range(1, len(acc) + 1)

# Accuracies
train_val_accuracy_figure = plt.figure()
plt.plot(number_of_completed_epochs, acc, label='Training acc')
plt.plot(number_of_completed_epochs, val_acc, label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.xlabel("Epochs")
plt.ylabel("Accuracy")
# plt.show()
train_val_accuracy_figure.savefig('../Results/g_r_%s/TrainingValidationAccuracy.png' % dt_string)
plt.close()

# Losses
train_val_loss_figure = plt.figure()
plt.plot(number_of_completed_epochs, loss, label='Training loss')
plt.plot(number_of_completed_epochs, val_loss, label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.xlabel("Epochs")
plt.ylabel("Loss")
# plt.show()
train_val_loss_figure.savefig('../Results/g_r_%s/TrainingValidationLoss.png' % dt_string)
plt.close()

# make positive and negative directory
if not os.path.exists('../Results/g_r_%s/PositiveResults/' % dt_string):
    os.mkdir('../Results/g_r_%s/PositiveResults/' % dt_string)

if not os.path.exists('../Results/g_r_%s/NegativeResults/' % dt_string):
    os.mkdir('../Results/g_r_%s/NegativeResults/' % dt_string)

# Plot original positive image
img_positive_tensor = getPositiveImages('Training/g_r_PositiveAll', 1, input_shape=input_shape)
positive_train_figure = plt.figure()
plt.imshow(img_positive_tensor[0])
# plt.show()
print(img_positive_tensor.shape)
positive_train_figure.savefig('../Results/g_r_%s/PositiveResults/PositiveTrainingFigure.png' % dt_string)
plt.close()

# Visualise Activations of positive image
visualiseActivations(img_positive_tensor, base_dir='../Results/g_r_%s/PositiveResults/' % dt_string)

# Plot original negative image
img_negative_tensor = getNegativeImages('Training/Negative', 1, input_shape=input_shape)
negative_train_figure = plt.figure()
plt.imshow(img_negative_tensor[0])
# plt.show()
print(img_negative_tensor.shape)
negative_train_figure.savefig('../Results/g_r_%s/NegativeResults/NegativeTrainingFigure.png' % dt_string)
plt.close()

# Visualise Activations of negative image
visualiseActivations(img_negative_tensor, base_dir='../Results/g_r_%s/NegativeResults/' % dt_string)

# Classifier evaluation
test_pos = getPositiveImages('Testing/g_r_PositiveAll', max_num_testing, input_shape)
test_neg = getNegativeImages('Testing/Negative', max_num_testing, input_shape)
testing_data, testing_labels, _ = makeImageSet(test_pos, test_neg, shuffle_needed=True)
print("Testing Data Shape:  " + str(testing_data.shape))
print("Testing Labels Shape: " + str(testing_labels.shape))
print("Got Unseen Testing data")

scores = classifier.evaluate(testing_data, testing_labels, batch_size=batch_size)
print("Test loss: %s" % scores[0])
print("Test accuracy: %s" % scores[1])

excel_headers.append("Test_Loss")
excel_dictionary.append(scores[0])
excel_headers.append("Test_Accuracy")
excel_dictionary.append(scores[1])

gettingTrueFalsePositiveNegatives(testing_data,
                                  testing_labels,
                                  text_file_path='../Results/g_r_%s/TrainingTestingResults/ActualPredictedMatrix.txt'
                                                 % dt_string,
                                  predicted_lenses_filepath='../Results/g_r_%s/TrainingTestingResults' % dt_string)

# Evaluate known 47 with negative 47
known_47_images = getUnseenData('UnseenData/Known47', max_num_prediction, input_shape=input_shape)
images_47, labels_47, des_47_names = makeImageSet(positive_images=list(known_47_images.values()),
                                                  known_des_names=list(known_47_images.keys()),
                                                  shuffle_needed=True)
print("47 Images Shape:  " + str(images_47.shape))
print("47 Labels Shape: " + str(labels_47.shape))
print("Got Unseen 47 data")

predicted_class_probabilities_47 = classifier.predict_classes(images_47, batch_size=batch_size)
lens_predicted_count_47 = np.count_nonzero(predicted_class_probabilities_47 == 1)
non_lens_predicted_count_47 = np.count_nonzero(predicted_class_probabilities_47 == 0)
print("%s/47 known images predicted" % lens_predicted_count_47)
print("%s/47 non lensed images predicted" % non_lens_predicted_count_47)

gettingTrueFalsePositiveNegatives(images_47,
                                  labels_47,
                                  text_file_path='../Results/g_r_%s/Predicted47/47_LensesPredicted.txt' % dt_string,
                                  predicted_lenses_filepath='../Results/g_r_%s/Predicted47' % dt_string)

savePredictedLenses(des_47_names,
                    predicted_class_probabilities_47,
                    predicted_lenses_filepath='../Results/g_r_%s/Predicted47' % dt_string,
                    text_file_path='../Results/g_r_%s/Predicted47/47_LensesPredicted.txt' % dt_string)

excel_headers.append("Predicted_Lens_47")
excel_dictionary.append(lens_predicted_count_47)
excel_headers.append("Predicted_No_Lens_47")
excel_dictionary.append(non_lens_predicted_count_47)

# Evaluate known 84 with negative 84
known_84_images = getUnseenData('UnseenData/Known84', max_num_prediction, input_shape=input_shape)
images_84, labels_84, des_84_names = makeImageSet(positive_images=list(known_84_images.values()),
                                                  known_des_names=list(known_84_images.keys()),
                                                  shuffle_needed=True)
print("84 Images Shape:  " + str(images_84.shape))
print("84 Labels Shape: " + str(labels_84.shape))
print("Got Unseen 84 data")

predicted_class_probabilities_84 = classifier.predict_classes(images_84, batch_size=batch_size)
lens_predicted_count_84 = np.count_nonzero(predicted_class_probabilities_84 == 1)
non_lens_predicted_count_84 = np.count_nonzero(predicted_class_probabilities_84 == 0)
print("%s/84 known images predicted" % lens_predicted_count_84)
print("%s/84 non lensed images predicted" % non_lens_predicted_count_84)

gettingTrueFalsePositiveNegatives(images_84,
                                  labels_84,
                                  text_file_path='../Results/g_r_%s/Predicted84/84_LensesPredicted.txt' % dt_string,
                                  predicted_lenses_filepath='../Results/g_r_%s/Predicted84' % dt_string)

savePredictedLenses(des_84_names,
                    predicted_class_probabilities_84,
                    predicted_lenses_filepath='../Results/g_r_%s/Predicted84' % dt_string,
                    text_file_path='../Results/g_r_%s/Predicted84/84_LensesPredicted.txt' % dt_string)

excel_headers.append("Predicted_Lens_84")
excel_dictionary.append(lens_predicted_count_84)
excel_headers.append("Predicted_No_Lens_84")
excel_dictionary.append(non_lens_predicted_count_84)

all_unseen_images = np.concatenate((images_47, images_84))
all_unseen_labels = np.concatenate((labels_47, labels_84))
all_des_names = np.concatenate((des_47_names, des_84_names))
print("All Data Shape: " + str(all_unseen_images.shape))
print("All Data Labels: " + str(all_unseen_labels.shape))

all_predicted_class_probabilities = classifier.predict_classes(all_unseen_images, batch_size=batch_size)
all_lens_predicted_count = np.count_nonzero(all_predicted_class_probabilities == 1)
all_non_lens_predicted_count = np.count_nonzero(all_predicted_class_probabilities == 0)
print("%s/131 known images predicted" % all_lens_predicted_count)
print("%s/131 non lensed images predicted" % all_non_lens_predicted_count)

gettingTrueFalsePositiveNegatives(all_unseen_images,
                                  all_unseen_labels,
                                  text_file_path='../Results/g_r_%s/PredictedAll/All_LensesPredicted.txt' % dt_string,
                                  predicted_lenses_filepath='../Results/g_r_%s/PredictedAll' % dt_string)

savePredictedLenses(all_des_names,
                    all_predicted_class_probabilities,
                    predicted_lenses_filepath='../Results/g_r_%s/PredictedAll' % dt_string,
                    text_file_path='../Results/g_r_%s/Predicted84/All_LensesPredicted.txt' % dt_string)

excel_headers.append("Predicted_Lens_All")
excel_dictionary.append(all_lens_predicted_count)
excel_headers.append("Predicted_No_Lens_All")
excel_dictionary.append(all_non_lens_predicted_count)

# K fold for training data
executeKFoldValidation(training_data, training_labels, val_data, val_labels, testing_data, testing_labels,
                       images_47, labels_47, images_84, labels_84, all_unseen_images, all_unseen_labels)

print("Test loss of normal CNN: %s" % scores[0])
print("Test accuracy of normal CNN: %s" % (scores[1] * 100))

# add row to excel table
if makeNewCSVFile:
    createExcelSheet('../Results/new_kerasCNN_Results.csv', excel_headers)
    writeToFile('../Results/new_kerasCNN_Results.csv', excel_dictionary)
else:
    writeToFile('../Results/new_kerasCNN_Results.csv', excel_dictionary)
