Optimum way to train a keras nn by a dataset
I would recommend running a script before even using Keras just to clean up your data into a more standard format such as .h5. You can then bin this data into batches with a given index in the key id. Then writing the DataGenerator in Keras becomes extremely easy. 

The way I do it is to have the following type of keys:

dataset_identifier_ix

where dataset is either train, validation or test, depending on the set you wish to use. Identifier in your case will be either image and label. Lastly, ix is the batch index. Inside each of these batches you have $n$ instances with which to train your model. 

If this is the structure of your .h5, then the DataGenerator code can be like

class DataGenerator(keras.utils.Sequence):

    'Generates data for Keras'
    def __init__(self, dataset, filename, X_identifier, Y_identifier, 
                 batch_size, percent_data_use = 1, shuffle=True):
        super(DataGenerator,self).__init__()

        with h5py.File(filename+'.h5', 'r') as hf:
            keys = list(hf.keys())
            # Get dimensions of the input space
            temp = [i for i in keys if dataset+'_'+X_identifier in i]
            x_dims = hf[temp[0]].shape[1::]
            num_files = len(temp)
            # Get dimensions of the output space
            temp = [i for i in keys if dataset+'_'+Y_identifier in i]
            y_dims = hf[temp[0]].shape[1::]

        # The batch size
        self.batch_size = batch_size
        self.num_files = num_files
        # Assumes the file ids are always from 0 to num_files
        self.file_ids = list(range(int(percent_data_use*num_files)))
        # Calculate the number of batches
        self.num_batches = int(percent_data_use*num_files)

        self.filename = filename
        self.dataset = dataset 
        self.X_identifier = X_identifier
        self.Y_identifier = Y_identifier

        # Dimensions of the input and the output
        self.x_dims = x_dims
        self.y_dims = y_dims
        self.input_shape = (int(x_dims[0]*sampling_percent),) + x_dims[1:]

        self.sampling_percent = sampling_percent
        self.shuffle = shuffle
        if shuffle is True: self.on_epoch_end()

    def __len__(self):
        """Number of batch in the Sequence.

        Returns
            The number of batches in the Sequence.
        """
        return self.num_batches

    def __getitem__(self, index):
        """Gets batch at position `index`.
        Arguments
            index: position of the batch in the Sequence.
        Returns
            A batch
        """        
        X = np.zeros((self.batch_size,) + self.x_dims)
        Y = np.zeros((self.batch_size,) + self.y_dims)

        with h5py.File(self.filename+'.h5', 'r') as hf:

            # Input and output identifiers
            x_id = self.dataset + '_' + \
                    self.X_identifier + '_' + str(self.file_ids[index])
            y_id = self.dataset + '_' + \
                    self.Y_identifier + '_' + str(self.file_ids[index])

            X = np.asarray(hf[x_id])
            Y = np.asarray(hf[y_id]) 

        if len(X) == 0:
            return None

        ## Restructure X, and Y for use in the Keras network
        return X, Y

    def on_epoch_end(self):
        'Updates indexes after each epoch'
        if self.shuffle == True:
            np.random.shuffle(self.file_ids)