cnn_keras.py 23.4 KB
Newer Older
1 2 3 4 5 6
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
"""
    Generic classifier with multiple models
    Models -> (Xception, VGG16, VGG19, ResNet50, InceptionV3, MobileNet)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
7

8 9 10 11 12 13
    Name: cnn_keras.py
    Author: Gabriel Kirsten Menezes (gabriel.kirsten@hotmail.com)

"""
import time
import os
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
14 15
import shutil
import random
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
16
import numpy as np
17
import json
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
18 19 20
import logging
import sys

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
21
from PIL import Image
22 23 24
from keras import applications
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
25
from keras.models import Model, load_model
26
from keras.layers import Dropout, Flatten, Dense
27 28
from keras.callbacks import ModelCheckpoint, TensorBoard
from keras import backend as K
29 30 31
from numpy import resize, expand_dims
from keras.preprocessing.image import load_img, img_to_array
        
32

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
33
from interface.interface import InterfaceException as IException
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
34
from classification.classifier import Classifier
35 36 37 38 39 40

from collections import OrderedDict

from util.config import Config
from util.file_utils import File
from util.utils import TimeUtils
41
        
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
42

43

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
44 45 46
logger = logging.getLogger('PIL')
logger.setLevel(logging.WARNING)

47 48 49 50 51 52 53
START_TIME = time.time()

# =========================================================
# Constants
# =========================================================

IMG_WIDTH, IMG_HEIGHT = 256, 256
54
weight_path = None
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
55

56 57 58 59 60 61 62
dict_preprocessing = {}
dict_preprocessing[0] = applications.xception.preprocess_input, applications.xception.decode_predictions
dict_preprocessing[1] = applications.vgg16.preprocess_input, applications.vgg16.decode_predictions
dict_preprocessing[2] = applications.vgg19.preprocess_input, applications.vgg19.decode_predictions
dict_preprocessing[3] = applications.resnet50.preprocess_input, applications.resnet50.decode_predictions
dict_preprocessing[4] = applications.inception_v3.preprocess_input, applications.inception_v3.decode_predictions
dict_preprocessing[5] = applications.mobilenet.preprocess_input, applications.mobilenet.decode_predictions
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
63

64
class CNNKeras(Classifier):
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
65 66
    """ Class for CNN classifiers based on Keras applications """

67
    def __init__(self, architecture="ResNet50", learning_rate=0.001, momentum=0.9, batch_size=32, epochs=50, fine_tuning_rate=100, transfer_learning=False, save_weights=True, perc_train=80, perc_validation=20, recreate_dataset=False):
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
68 69 70 71
        """
            Constructor of CNNKeras
        """

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
        self.architecture = Config(
            "Architecture", architecture, str)
        self.learning_rate = Config(
            "Learning rate", learning_rate, float)
        self.momentum = Config(
            "Momentum", momentum, float)
        self.batch_size = Config(
            "Batch size", batch_size, int)
        self.epochs = Config(
            "Epochs", epochs, int)
        self.fine_tuning_rate = Config(
            "Fine Tuning Rate", fine_tuning_rate, int)
        self.transfer_learning = Config(
            "Transfer Learning", transfer_learning, bool)
        self.save_weights = Config(
            "Save weights", save_weights, bool)
88 89 90 91 92 93
        self.perc_train = Config(
            "Perc Train", perc_train, float)
        self.perc_validation = Config(
            "Perc Validation", perc_validation, float)
        self.recreate_dataset = Config(
            "Recreate Dataset", recreate_dataset, bool)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
94
        self.file_name = "kerasCNN"
95

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
96
        self.model = None
97

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
98
        self.trained = False
99 100

    def get_config(self):
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
101
        """Return configuration of classifier.
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
102

103 104 105 106 107
        Returns
        -------
        config : OrderedDict
            Current configs of classifier.
        """
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
108 109
        keras_config = OrderedDict()

110 111 112 113 114 115 116 117
        keras_config["Architecture"] = self.architecture
        keras_config["Learning rate"] = self.learning_rate
        keras_config["Momentum"] = self.momentum
        keras_config["Batch size"] = self.batch_size
        keras_config["Epochs"] = self.epochs
        keras_config["Fine Tuning rate"] = self.fine_tuning_rate
        keras_config["Transfer Learning"] = self.transfer_learning
        keras_config["Save weights"] = self.save_weights
118 119 120
        keras_config["Perc Train"] = self.perc_train
        keras_config["Perc Validation"] = self.perc_validation
        keras_config["Recreate Dataset"] = self.recreate_dataset
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
121
        return keras_config
122 123

    def set_config(self, configs):
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
124
        """Update configuration of classifier.
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
125

126 127 128 129 130
        Parameters
        ----------
        configs : OrderedDict
            New configs of classifier.
        """
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
131 132 133 134
        self.architecture = Config.nvl_config(
            configs["Architecture"], self.architecture)
        self.learning_rate = Config.nvl_config(
            configs["Learning rate"], self.learning_rate)
135
        self.momentum = Config.nvl_config(configs["Momentum"], self.momentum)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
136 137
        self.batch_size = Config.nvl_config(
            configs["Batch size"], self.batch_size)
138
        self.epochs = Config.nvl_config(configs["Epochs"], self.epochs)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
139 140 141 142 143 144 145 146 147 148 149 150
        self.fine_tuning_rate = Config.nvl_config(
            configs["Fine Tuning rate"], self.fine_tuning_rate)
        self.transfer_learning = Config.nvl_config(
            configs["Transfer Learning"], self.transfer_learning)
        self.save_weights = Config.nvl_config(
            configs["Save weights"], self.save_weights)
        self.perc_train = Config.nvl_config(
            configs["Perc Train"], self.perc_train)
        self.perc_validation = Config.nvl_config(
            configs["Perc Validation"], self.perc_validation)
        self.recreate_dataset = Config.nvl_config(
            configs["Recreate Dataset"], self.recreate_dataset)
151 152

    def get_summary_config(self):
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
153
        """Return fomatted summary of configuration.
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
154

155 156 157 158 159
        Returns
        -------
        summary : string
            Formatted string with summary of configuration.
        """
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
160
        keras_config = OrderedDict()
161

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
162
        keras_config[self.architecture.label] = self.architecture.value
163 164 165 166
        keras_config[self.learning_rate.label] = self.learning_rate.value
        keras_config[self.momentum.label] = self.momentum.value
        keras_config[self.batch_size.label] = self.batch_size.value
        keras_config[self.epochs.label] = self.epochs.value
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
167
        keras_config[self.fine_tuning_rate.label] = self.fine_tuning_rate.value
168 169
        keras_config[self.transfer_learning.label] = self.transfer_learning.value
        keras_config[self.save_weights.label] = self.save_weights.value
170 171 172
        keras_config[self.perc_train.label] = self.perc_train.value
        keras_config[self.perc_validation.label] = self.perc_validation.value
        keras_config[self.recreate_dataset.label] = self.recreate_dataset.value
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
173 174 175
        summary = ''
        for config in keras_config:
            summary += "%s: %s\n" % (config, str(keras_config[config]))
176

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
177
        return summary
178

179
    def classify(self, dataset, test_dir, test_data, image):
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
180
        """"Perform the classification.
181

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
182 183 184 185 186 187 188 189
        Parameters
        ----------
        dataset : string
            Path to image dataset.
        test_dir : string
            Not used.
        test_data : string
            Name of test data file.
190

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
191 192 193 194 195 196 197 198
        Returns
        -------
        summary : list of string
            List of predicted classes for each instance in test data in ordered way.
        """

        predict_directory = File.make_path(dataset, test_dir)

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
199
        # Create a Keras class
200 201 202 203 204 205
        if not os.path.exists(File.make_path(predict_directory, "png")):
            os.makedirs(File.make_path(predict_directory, "png"))

        for file in os.listdir(predict_directory):
            print(File.make_path(predict_directory, file))
            if os.path.splitext(file)[-1] == ".tif":
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
206 207 208 209 210 211
                try:
                    img = Image.open(File.make_path(predict_directory, file))
                    # img.thumbnail(img.size)
                    new_file = os.path.splitext(file)[0] + ".png"
                    img.save(File.make_path(predict_directory,
                                            'png', new_file), "PNG", quality=100)
212 213
                except Exception as e:
                    print(e)
214
            else:
215
                print(File.make_path(predict_directory, file))
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
216 217
                os.symlink(File.make_path(predict_directory, file),
                           File.make_path(predict_directory, 'png', file))
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
218

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
219 220 221
        classify_datagen = ImageDataGenerator()

        classify_generator = classify_datagen.flow_from_directory(
222
            File.make_path(predict_directory, 'png'),
223
            target_size=(IMG_HEIGHT, IMG_WIDTH),
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
224 225 226 227 228
            batch_size=1,
            shuffle=False,
            class_mode=None)

        try:
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
229 230
            # self.model.load_weights(
            #"../models_checkpoints/" + self.file_name + ".h5")
231 232 233
            K.clear_session()
            if self.weight_path is not None:
                self.model = load_model(self.weight_path)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
234 235
                path_classes = self.weight_path.replace(
                    "_model.h5", "_classes.npy")
236
                print("Load Model H5:"+self.weight_path)
237
                CLASS_NAMES = np.load(path_classes).item().keys()
238
        except Exception as e:
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
239
            raise IException("Can't load the model in " +
240
                             self.weight_path + str(e))
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
241

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
242
        output_classification = self.model.predict_generator(
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
243
            classify_generator, classify_generator.samples, verbose=2)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
244 245 246

        one_hot_output = np.argmax(output_classification, axis=1)

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
247 248
        one_hot_output = one_hot_output.tolist()

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
        for index in range(0, len(one_hot_output)):
            one_hot_output[index] = CLASS_NAMES[one_hot_output[index]]

        return one_hot_output

    def train(self, dataset, training_data, force=False):
        """Perform the training of classifier.

        Parameters
        ----------
        dataset : string
            Path to image dataset.
        training_data : string
            Name of ARFF training file.
        force : boolean, optional, default = False
            If False don't perform new training if there is trained data.
        """
266

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
267
        # select .h5 filename
268 269 270
        if self.fine_tuning_rate.value == 100:
            self.file_name = str(self.architecture.value) + \
                '_learning_rate' + str(self.learning_rate.value) + \
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
271
                '_transfer_learning'
272 273 274
        elif self.fine_tuning_rate.value == -1:
            self.file_name = str(self.architecture.value) + \
                '_learning_rate' + str(self.learning_rate.value) + \
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
275 276
                '_without_transfer_learning'
        else:
277 278
            self.file_name = str(self.architecture.value) + \
                '_learning_rate' + str(self.learning_rate.value) + \
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
279
                '_fine_tunning_' + str(self.fine_tuning_rate.value)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
280

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
281 282
        File.remove_dir(File.make_path(dataset, ".tmp"))

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
283 284
        train_generator, validation_generator, test_generator = self.make_dataset(
            dataset)
285 286 287 288 289

        # Save the model according to the conditions
        if self.save_weights:
            if not os.path.exists("../models_checkpoints/"):
                os.makedirs("../models_checkpoints/")
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
290

291
            checkpoint = ModelCheckpoint("../models_checkpoints/" + self.file_name + ".h5", monitor='val_acc',
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
292 293
                                         verbose=1, save_best_only=True, save_weights_only=False,
                                         mode='auto', period=1)
294 295 296
        else:
            checkpoint = None

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
297 298
        self.model = self.select_model_params(train_generator.num_classes)

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
299 300 301
        tensorboard = TensorBoard(
            log_dir="../models_checkpoints/logs_" + self.file_name, write_images=False)
        # tensorboard.set_model(self.model)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
302 303 304 305 306
        # compile the model
        self.model.compile(loss="categorical_crossentropy",
                           optimizer=optimizers.SGD(
                               lr=self.learning_rate.value, momentum=self.momentum.value),
                           metrics=["accuracy"])
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
307

308
        # Train the model
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
309
        self.model.fit_generator(
310
            train_generator,
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
311 312
            steps_per_epoch=train_generator.samples // self.batch_size.value,
            epochs=self.epochs.value,
313 314 315
            callbacks=[checkpoint, tensorboard],
            validation_data=validation_generator,
            validation_steps=validation_generator.samples // self.batch_size.value)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
316 317

        if self.save_weights:
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
318
            # self.model.save_weights(
319 320 321 322 323 324
            #    "../models_checkpoints/" + self.file_name + ".h5")
            self.model.save(
                "../models_checkpoints/" + self.file_name + "_model.h5")
            self.weight_path = "../models_checkpoints/" + self.file_name + "_model.h5"

            dict_classes = validation_generator.class_indices
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
325 326
            np.save("../models_checkpoints/" + self.file_name +
                    "_classes.npy", dict_classes)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
327 328

    def must_train(self):
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
329
        """Return if classifier must be trained.
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
330 331 332 333 334

        Returns
        -------
        True
        """
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
335
        return not self.trained
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
336 337

    def must_extract_features(self):
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
338
        """Return if classifier must be extracted features.
339

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
340 341 342 343 344
        Returns
        -------
        False
        """
        return False
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
345 346 347 348

    def select_model_params(self, num_classes):
        if self.fine_tuning_rate.value != -1:
            if self.architecture.value == "Xception":
349
                self.app = 0
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
350 351 352
                model = applications.Xception(
                    weights="imagenet", include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            elif self.architecture.value == "VGG16":
353
                self.app = 1
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
354 355 356
                model = applications.VGG16(
                    weights="imagenet", include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            elif self.architecture.value == "VGG19":
357
                self.app = 2
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
358 359 360
                model = applications.VGG19(
                    weights="imagenet", include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            elif self.architecture.value == "ResNet50":
361
                self.app = 3
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
362 363 364
                model = applications.ResNet50(
                    weights="imagenet", include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            elif self.architecture.value == "InceptionV3":
365
                self.app = 4
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
366 367 368
                model = applications.InceptionV3(
                    weights="imagenet", include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            elif self.architecture.value == "MobileNet":
369
                self.app = 5
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
370 371 372
                model = applications.MobileNet(
                    weights="imagenet", include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))

373
            for layer in model.layers[:int(len(model.layers) * (self.fine_tuning_rate.value / 100.0))]:
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
374 375 376 377
                layer.trainable = False

        else:  # without transfer learning
            if self.architecture.value == "Xception":
378
                self.app = 0
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
379 380 381
                model = applications.Xception(
                    weights=None, include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            elif self.architecture.value == "VGG16":
382
                self.app = 1
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
383 384 385
                model = applications.VGG16(
                    weights=None, include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            elif self.architecture.value == "VGG19":
386
                self.app = 2
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
387 388 389
                model = applications.VGG19(
                    weights=None, include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            elif self.architecture.value == "ResNet50":
390
                self.app = 3
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
391 392 393
                model = applications.ResNet50(
                    weights=None, include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            elif self.architecture.value == "InceptionV3":
394
                self.app = 4
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
395 396 397
                model = applications.InceptionV3(
                    weights=None, include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            elif self.architecture.value == "MobileNet":
398
                self.app = 5
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
                model = applications.MobileNet(
                    weights=None, include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
            for layer in model.layers:
                layer.trainable = True

        # Adding custom Layers
        new_custom_layers = model.output
        new_custom_layers = Flatten()(new_custom_layers)
        new_custom_layers = Dense(1024, activation="relu")(new_custom_layers)
        new_custom_layers = Dropout(0.5)(new_custom_layers)
        new_custom_layers = Dense(1024, activation="relu")(new_custom_layers)
        predictions = Dense(num_classes,
                            activation="softmax")(new_custom_layers)

        # creating the final model
        model = Model(inputs=model.input, outputs=predictions)

        return model

    def make_dataset(self, dataset):

        # create symbolic links to the dataset
421 422
        KERAS_DATASET_DIR_NAME = ".keras_dataset"
        #KERAS_DATASET_DIR_NAME = File.make_path("..", os.path.split(dataset)[-1] + "_keras_dataset")
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
423 424
        KERAS_DIR_TRAIN_NAME = "train"
        KERAS_DIR_VALIDATION_NAME = "validation"
425 426 427 428
        KERAS_DIR_TEST_NAME = "test"
        PERC_TRAIN = self.perc_train.value
        PERC_VALIDATION = self.perc_validation.value

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
429
        # create keras dir dataset
430 431 432 433
        if not os.path.exists(File.make_path(dataset, KERAS_DATASET_DIR_NAME)) or self.recreate_dataset.value:
            if os.path.exists(File.make_path(dataset, KERAS_DATASET_DIR_NAME)):
                shutil.rmtree(File.make_path(dataset, KERAS_DATASET_DIR_NAME))

Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
434 435
            os.makedirs(File.make_path(dataset, KERAS_DATASET_DIR_NAME))

436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
            # create keras dir train
            if not os.path.exists(File.make_path(dataset, KERAS_DATASET_DIR_NAME, KERAS_DIR_TRAIN_NAME)):
                os.makedirs(File.make_path(
                    dataset, KERAS_DATASET_DIR_NAME, KERAS_DIR_TRAIN_NAME))

            # create keras dir validation
            if not os.path.exists(File.make_path(dataset, KERAS_DATASET_DIR_NAME, KERAS_DIR_VALIDATION_NAME)):
                os.makedirs(File.make_path(
                    dataset, KERAS_DATASET_DIR_NAME, KERAS_DIR_VALIDATION_NAME))

            # create keras dir test
            if not os.path.exists(File.make_path(dataset, KERAS_DATASET_DIR_NAME, KERAS_DIR_TEST_NAME)):
                os.makedirs(File.make_path(
                    dataset, KERAS_DATASET_DIR_NAME, KERAS_DIR_TEST_NAME))

            dir_classes = sorted(File.list_dirs(dataset))

            if KERAS_DATASET_DIR_NAME in dir_classes:
                dir_classes.remove(KERAS_DATASET_DIR_NAME)

            for dir_class in dir_classes:
                root = File.make_path(dataset, dir_class)
                files = os.listdir(root)
                random.shuffle(files)
                quant_files = len(files)
                quant_train = int((quant_files / 100.0) * PERC_TRAIN)
                quant_validation = int((quant_files / 100.0) * PERC_VALIDATION)

                files_train = files[0:quant_train]
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
465 466 467 468 469
                files_validation = files[quant_train:quant_train +
                                         quant_validation]
                files_test = files[quant_train + quant_validation:quant_files]
                print("Processing class %s - %d itens - %d train items - %d validation items" %
                      (dir_class, quant_files, quant_train, quant_validation))
470 471

                for file in files_train:
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
472 473
                    dir_class_train = File.make_path(
                        dataset, KERAS_DATASET_DIR_NAME, KERAS_DIR_TRAIN_NAME, dir_class)
474 475
                    if not os.path.exists(dir_class_train):
                        os.makedirs(dir_class_train)
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
476

477 478
                    if os.path.splitext(file)[-1] == ".tif":
                        img = Image.open(File.make_path(root, file))
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
479 480 481 482
                        # img.thumbnail(img.size)
                        new_file = os.path.splitext(file)[0] + ".png"
                        img.save(File.make_path(dir_class_train,
                                                new_file), "PNG", quality=100)
483
                    else:
484 485 486
                        print(100*'-')
                        print(File.make_path(root, file))
                        print(100*'-')
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
487 488
                        os.symlink(File.make_path(root, file),
                                   File.make_path(dir_class_train, file))
489 490

                for file in files_validation:
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
491 492
                    dir_class_validation = File.make_path(
                        dataset, KERAS_DATASET_DIR_NAME, KERAS_DIR_VALIDATION_NAME, dir_class)
493
                    if not os.path.exists(dir_class_validation):
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
494 495
                        os.makedirs(dir_class_validation)

496 497
                    if os.path.splitext(file)[-1] == ".tif":
                        img = Image.open(File.make_path(root, file))
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
498 499 500 501
                        # img.thumbnail(img.size)
                        new_file = os.path.splitext(file)[0] + ".png"
                        img.save(File.make_path(dir_class_validation,
                                                new_file), "PNG", quality=100)
502
                    else:
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
503 504
                        os.symlink(File.make_path(root, file),
                                   File.make_path(dir_class_validation, file))
505 506

                for file in files_test:
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
507 508
                    dir_class_test = File.make_path(
                        dataset, KERAS_DATASET_DIR_NAME, KERAS_DIR_TEST_NAME, dir_class)
509
                    if not os.path.exists(dir_class_test):
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
510 511
                        os.makedirs(dir_class_test)

512 513
                    if os.path.splitext(file)[-1] == ".tif":
                        img = Image.open(File.make_path(root, file))
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
514 515 516 517
                        # img.thumbnail(img.size)
                        new_file = os.path.splitext(file)[0] + ".png"
                        img.save(File.make_path(dir_class_test,
                                                new_file), "PNG", quality=100)
518
                    else:
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
519 520
                        os.symlink(File.make_path(root, file),
                                   File.make_path(dir_class_test, file))
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
521 522 523 524

        train_datagen = ImageDataGenerator()

        train_generator = train_datagen.flow_from_directory(
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
525 526
            File.make_path(dataset, KERAS_DATASET_DIR_NAME,
                           KERAS_DIR_TRAIN_NAME),
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
527 528 529 530 531 532 533 534
            target_size=(IMG_HEIGHT, IMG_WIDTH),
            batch_size=self.batch_size.value,
            shuffle=True,
            class_mode="categorical")

        validation_datagen = ImageDataGenerator()

        validation_generator = validation_datagen.flow_from_directory(
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
535 536
            File.make_path(dataset, KERAS_DATASET_DIR_NAME,
                           KERAS_DIR_VALIDATION_NAME),
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
537 538 539 540 541
            target_size=(IMG_HEIGHT, IMG_WIDTH),
            batch_size=self.batch_size.value,
            shuffle=True,
            class_mode="categorical")

542 543 544
        test_datagen = ImageDataGenerator()

        test_generator = test_datagen.flow_from_directory(
Gabriel Kirsten's avatar
 
Gabriel Kirsten committed
545 546
            File.make_path(dataset, KERAS_DATASET_DIR_NAME,
                           KERAS_DIR_TEST_NAME),
547 548 549 550 551 552
            target_size=(IMG_HEIGHT, IMG_WIDTH),
            batch_size=self.batch_size.value,
            shuffle=True,
            class_mode="categorical")

        return train_generator, validation_generator, test_generator
553 554

    def single_classify(self, image_path, directory, extractors, dict_classes):
555 556 557 558 559 560 561 562 563
        preprocess_input, decode_predictions = dict_preprocessing[self.app]
        pil_image = load_img(image_path)
        np_image = img_to_array(pil_image)
        res_image = resize(np_image, (IMG_HEIGHT, IMG_WIDTH, 3))
        tensor = expand_dims(res_image, axis=0)
        tensor = preprocess_input(tensor)
        predict = self.model.predict(tensor)
        predict = np.argmax(predict, axis=1)
        return dict_classes[predict[0]]