"""
# PACKAGES AND LIBRARIES
"""
"""
#### GENERAL
"""
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
"""
#### PATH
"""
import os
import os.path
from pathlib import Path
import glob
"""
#### IMAGE PROCESS
"""
from PIL import Image
from keras.preprocessing import image
from tensorflow.keras.preprocessing.image import ImageDataGenerator
"""
#### SCALER & TRANSFORMATION
"""
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler
from keras.utils.np_utils import to_categorical
from sklearn.model_selection import train_test_split
from keras import regularizers
"""
#### ACCURACY CONTROL
"""
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, roc_auc_score, roc_curve
"""
#### OPTIMIZER
"""
from keras.optimizers import RMSprop,Adam,Optimizer
"""
#### MODEL LAYERS
"""
from tensorflow.keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPool2D, BatchNormalization,MaxPooling2D,BatchNormalization,\
                        Permute, TimeDistributed, Bidirectional,GRU, SimpleRNN, LSTM, GlobalAveragePooling2D
from keras import models
from keras import layers
import tensorflow as tf
from keras.applications import VGG16,VGG19
"""
#### IGNORING WARNINGS
"""
from warnings import filterwarnings

filterwarnings("ignore",category=DeprecationWarning)
filterwarnings("ignore", category=FutureWarning) 
filterwarnings("ignore", category=UserWarning)
"""
# PATH
"""
Brain_CT_Path = Path("../input/brain-ct-hemorrhage-dataset/Data")
"""
* file path is determined
"""
JPG_Path = list(Brain_CT_Path.glob(r"**/*.jpg"))
"""
* all images in the file path are assigned to a list
"""
"""
# LABEL
"""
JPG_Labels = list(map(lambda x: os.path.split(os.path.split(x)[0])[1],JPG_Path))
"""
* the categories of the images are separated
"""
"""
# TRANSFORMATION TO SERIES
"""
JPG_Path_Series = pd.Series(JPG_Path,name="JPG").astype(str)
JPG_Labels_Series = pd.Series(JPG_Labels,name="CATEGORY")
"""
* it is converted to Series structure before it is converted to DataFrame
"""
"""
# TRANSFORMATION TO DATAFRAME
"""
Main_Data = pd.concat([JPG_Path_Series,JPG_Labels_Series],axis=1)
"""
* it is converted to DataFrame
"""
print(Main_Data.head(-1))
"""
#### REPLACING
"""
Main_Data["CATEGORY"].replace({"11[11]":"Hemorrhage","11[11]":"Hemorrhage","12[12]":"Hemorrhage","13[13]":"Hemorrhage",
                               "14[14]":"Hemorrhage","15[15]":"Hemorrhage","17[17]__":"Hemorrhage",
                               "19[19]":"Hemorrhage","1[1]":"Hemorrhage","20[20]_2":"Hemorrhage",
                               "21[21] _2":"Hemorrhage","2[2]":"Hemorrhage","3[3]":"Hemorrhage","4[4]":"Hemorrhage","5[5]":"Hemorrhage",
                               "6[6]":"Hemorrhage","7[7]":"Hemorrhage","8[8]":"Hemorrhage","9[9]":"Hemorrhage"},inplace=True)
Main_Data["CATEGORY"].replace({"N10[N10]":"Normal","N11[N11]":"Normal","N12[N12]":"Normal","N13[N13]":"Normal","N14[N14]":"Normal",
                               "N15[N15]":"Normal","N15[N15]":"Normal",
                               "N16[N16]":"Normal","N17[N17]":"Normal","N18[N18]":"Normal",
                               "N19[N19]":"Normal","N1[N1]":"Normal","N20[N20]":"Normal","N21[N21]":"Normal",
                               "N22[N22]":"Normal","N23[N23]":"Normal","N24[N24]":"Normal",
                               "N25[N25]":"Normal","N26[N26]":"Normal","N27[N27]":"Normal","N2[N2]":"Normal",
                               "N3[N3]":"Normal","N4[N4]":"Normal","N5[N5]":"Normal",
                               "N6[N6]":"Normal","N7[N7]":"Normal","N8[N8]":"Normal","N9[N9]":"Normal"},inplace=True)
"""
* we have to change the names because the categories in the data are complex
"""
print(Main_Data.head(-1))
print(Main_Data["CATEGORY"].value_counts())
"""
# SHUFFLING
"""
Main_Data = Main_Data.sample(frac=1).reset_index(drop=True)
"""
* we have to mix the data to increase the success of the model and maintain its objectivity.
"""
print(Main_Data.head(-1))
"""
# VISUALIZATION
"""
plt.style.use('dark_background')
sns.countplot(Main_Data["CATEGORY"])
plt.show()
Main_Data['CATEGORY'].value_counts().plot.pie(figsize=(5,5))
plt.show()
sns.histplot(Main_Data['CATEGORY'].index)
plt.show()
figure = plt.figure(figsize=(10,10))
x = plt.imread(Main_Data["JPG"][0])
plt.imshow(x)
plt.xlabel(x.shape)
plt.title(Main_Data["CATEGORY"][0])
figure = plt.figure(figsize=(10,10))
x = plt.imread(Main_Data["JPG"][25])
plt.imshow(x)
plt.xlabel(x.shape)
plt.title(Main_Data["CATEGORY"][6769])
fig, axes = plt.subplots(nrows=5,
                        ncols=5,
                        figsize=(10,10),
                        subplot_kw={"xticks":[],"yticks":[]})

for i,ax in enumerate(axes.flat):
    ax.imshow(plt.imread(Main_Data["JPG"][i]))
    ax.set_title(Main_Data["CATEGORY"][i])
plt.tight_layout()
plt.show()
"""
# DETERMINATION TRAIN AND TEST DATA
"""
Train_Data,Test_Data = train_test_split(Main_Data,train_size=0.9,shuffle=True,random_state=42)
"""
* we divided it into test and training set
* we set the shuffle parameter to True for training quality
* we told it to use the same data as random state
"""
print("TRAIN SHAPE: ",Train_Data.shape)
print("TEST SHAPE: ",Test_Data.shape)
print(Train_Data.head(-1))
print("----"*20)
print(Test_Data.head(-1))
"""
# IMAGE GENERATOR
"""
Generator = ImageDataGenerator(rescale=1./255,
                               zoom_range=0.2,
                              shear_range=0.2,
                              rotation_range=40,
                              horizontal_flip=True,
                               fill_mode="nearest",
                              validation_split=0.1)
"""
* we used diversification so that the model does not shift to the overfitting orientation
"""
Test_Generator = ImageDataGenerator(rescale=1./255)
"""
* we don't need diversification for test data, we will use it as it is
"""
"""
#### How Generator Applied Image Look Like
"""
example_Image = Train_Data["JPG"][99]
Load_Image = image.load_img(example_Image,target_size=(200,200))
Array_Image = image.img_to_array(Load_Image)
Array_Image = Array_Image.reshape((1,) + Array_Image.shape)

i = 0
for batch in Generator.flow(Array_Image,batch_size=1):
    plt.figure(i)
    IMG = plt.imshow(image.array_to_img(batch[0]))
    i += 1
    if i % 4 == 0:
        break
plt.show()
"""
#### APPLYING GENERATOR AND TRANSFORMATION TO TENSOR
"""
Train_IMG_Set = Generator.flow_from_dataframe(dataframe=Train_Data,
                                             x_col="JPG",
                                             y_col="CATEGORY",
                                             color_mode="grayscale",
                                             class_mode="categorical",
                                             subset="training")
Validation_IMG_Set = Generator.flow_from_dataframe(dataframe=Train_Data,
                                                  x_col="JPG",
                                                  y_col="CATEGORY",
                                                  color_mode="grayscale",
                                                  class_mode="categorical",
                                                  subset="validation")
Test_IMG_Set = Generator.flow_from_dataframe(dataframe=Test_Data,
                                                 x_col="JPG",
                                                 y_col="CATEGORY",
                                                 color_mode="grayscale",
                                                 class_mode="categorical")
"""
#### CHECKING
"""
for data_batch,label_batch in Train_IMG_Set:
    print("DATA SHAPE: ",data_batch.shape)
    print("LABEL SHAPE: ",label_batch.shape)
    break
for data_batch,label_batch in Validation_IMG_Set:
    print("DATA SHAPE: ",data_batch.shape)
    print("LABEL SHAPE: ",label_batch.shape)
    break
print("TRAIN: ")
print(Train_IMG_Set.class_indices)
print(Train_IMG_Set.classes[0:5])
print(Train_IMG_Set.image_shape)
print("---"*20)
print("VALIDATION: ")
print(Validation_IMG_Set.class_indices)
print(Validation_IMG_Set.classes[0:5])
print(Validation_IMG_Set.image_shape)
print("---"*20)
print("TEST: ")
print(Test_IMG_Set.batch_size)
print(Test_IMG_Set.image_shape)
"""
# CNN STRUCTURE WITH LSTM / RCNN
"""
Model = Sequential()

Model.add(Conv2D(12,(3,3),activation="relu",
                 input_shape=(256,256,1)))
Model.add(BatchNormalization())
Model.add(MaxPooling2D((2,2)))

#
Model.add(Conv2D(24,(3,3),
                 activation="relu",padding="same"))
Model.add(Dropout(0.2))
Model.add(MaxPooling2D((2,2)))

#
Model.add(Conv2D(64,(3,3),
                 activation="relu",padding="same"))
Model.add(Dropout(0.5))
Model.add(MaxPooling2D((2,2)))


#
Model.add(TimeDistributed(Flatten()))
Model.add(Bidirectional(LSTM(32,
                                  return_sequences=True,
                                  dropout=0.5,
                                  recurrent_dropout=0.5)))
Model.add(Bidirectional(GRU(32,
                                  return_sequences=True,
                                  dropout=0.5,
                                  recurrent_dropout=0.5)))

#
Model.add(Flatten())
Model.add(Dense(256,activation="relu"))
Model.add(Dropout(0.5))
Model.add(Dense(2,activation="softmax"))
"""
* LSTM and GRU are iterative layers
"""
"""
* LSTM and GRU serve to inject past information into the future, thereby reducing the gradient destruction problem
"""
"""
* we used LSTM and GRU layers both with fully-connetted layers and Conv2D
* RCNN structure is created in this way
* we determined the LSTM and GRU layers as bidirectional
"""
"""
* less problem of gradient disappearance in LSTM and GRU
"""
"""
* we used it with Dropout so that the model does not shift to overfitting orientation
* we made return_success True because we wanted each process to generate output separately
"""
"""
* we also used dropout within the GRU and LSTM layers to prevent the model from shifting to the overfitting orientation
* recurrent_dropout means transmission damping ratio of iterative layers
"""
"""
Loss Function We Used:


![](https://gombru.github.io/assets/cross_entropy_loss/intro.png)
"""
"""
* Activation Function:

![](http://rasbt.github.io/mlxtend/user_guide/general_concepts/activation-functions_files/activation-functions.png)
"""
Call_Back = tf.keras.callbacks.EarlyStopping(monitor="loss",patience=5,mode="min")
"""
* we wanted the training of the model to stop where the loss value is minimal
"""
Model.compile(optimizer="rmsprop",loss="categorical_crossentropy",metrics=["accuracy"])
CNN_Model = Model.fit(Train_IMG_Set,
                      validation_data=Validation_IMG_Set,
                            callbacks=Call_Back,
                      epochs=50)
"""
#### CHECKING
"""
Model_Results = Model.evaluate(Test_IMG_Set,verbose=False)
print("LOSS:  " + "%.4f" % Model_Results[0])
print("ACCURACY:  " + "%.2f" % Model_Results[1])
print(Model.summary())
plt.plot(CNN_Model.history["accuracy"])
plt.plot(CNN_Model.history["val_accuracy"])
plt.ylabel("ACCURACY")
plt.legend()
plt.show()
plt.plot(CNN_Model.history["loss"])
plt.plot(CNN_Model.history["val_loss"])
plt.ylabel("LOSS")
plt.legend()
plt.show()
plt.plot(CNN_Model.history["loss"])
plt.plot(CNN_Model.history["accuracy"])
plt.ylabel("LOSS - ACCURACY")
plt.legend()
plt.show()
plt.plot(CNN_Model.history["val_loss"])
plt.plot(CNN_Model.history["val_accuracy"])
plt.ylabel("VAL LOSS - VAL ACCURACY")
plt.legend()
plt.show()
Dict_Summary = pd.DataFrame(CNN_Model.history)
Dict_Summary.plot()

"""
#### PREDICTION
"""
Prediction = Model.predict(Test_IMG_Set)
Prediction = Prediction.argmax(axis=-1)
fig, axes = plt.subplots(nrows=5,
                         ncols=5,
                         figsize=(20, 20),
                        subplot_kw={'xticks': [], 'yticks': []})

for i, ax in enumerate(axes.flat):
    ax.imshow(plt.imread(Test_Data["JPG"].iloc[i]))
    ax.set_title(f"PREDICTION:{Prediction[i]}")
plt.tight_layout()
plt.show()