# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os,sys,shutil
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames[:30]:
        print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All" 
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
# Load the libraries

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
import seaborn as sns
import cv2
from sklearn.model_selection import train_test_split
# pd.set_option('display.max_colwidth',0)
# Load the data and url

train_url = r'/kaggle/input/age-prediction-dataset-indian-actors/train/' 
test_url = r'/kaggle/input/age-prediction-dataset-indian-actors/test/'

train_dir = train_url + 'Train/'
test_dir = test_url + 'Test/'

train_data  = pd.read_csv(train_url + 'train.csv') 
test_data = pd.read_csv(test_url + 'test.csv')
# Train data
print('Shape of the train data - ',train_data.shape)
train_data.head()
# Manipulating the dataframe
train_data1=train_data.copy()
train_data1['loc'] = train_dir + train_data['ID']
train_data1.head()
# Show the image present in the data
def read_img(data):
    classes = ['YOUNG',"MIDDLE",'OLD']
    for c in classes:
            img = plt.imread(data[data['Class']==c].head(1)['loc'].values[0])
            print('Shape of the image -',img.shape)
            plt.title(c)
            plt.imshow(img)
            plt.show()
read_img(train_data1)
# Train Data
train_data1.drop(columns= ['ID'],inplace=True)
train_data1 = train_data1[['loc','Class']].copy()
train_data1.head()
# Visulaizing the number of images in the particular class
train_data1.groupby('Class').count().plot(kind='bar',figsize=(20,10))
train_data1['Class'].replace(['YOUNG','MIDDLE','OLD'],[0,1,2],inplace=True)
new_images = []
new_classes = []
for img_loc,classes in zip(train_data1['loc'],train_data1['Class']):
    img = cv2.imread(img_loc,flags=0)
    img_resize = cv2.resize(img,(32,32))
    new_images.append(img_resize.astype('float32'))
    new_classes.append(classes)

plt.imshow(new_images[0])
plt.title(new_classes[0])
# Code to Image Data Generator
generator = tf.keras.preprocessing.image.ImageDataGenerator(rotation_range=15,
                                                            horizontal_flip=True,
                                                            data_format='channels_last')

# generator.flow_from_dataframe() ____________________Please check this one! Exclusive!
# Train sets
train_x = np.stack(new_images)
train_y = np.stack(new_classes)
# Splitting the data set
X_train,X_val,y_train,y_val = train_test_split(train_x,train_y,test_size=0.2,random_state=42)

X_train.shape,y_train.shape,X_val.shape,y_val.shape
# Manipulating the target values
y_train = tf.keras.utils.to_categorical(y_train,num_classes=3)
y_val = tf.keras.utils.to_categorical(y_val,num_classes=3)
"""
# Modeling Part
"""
# Assemble the model components --->>> Keras Framework
from keras import Sequential
from keras.callbacks import EarlyStopping,ModelCheckpoint
from keras import Input,regularizers,optimizers,losses,metrics,layers
from keras import Model,optimizers
from keras.utils import plot_model
from keras.losses import CategoricalCrossentropy
train_x.shape
"""
# Data Augmentation
"""
# Augmentation on the data
data_augmentation = Sequential([
    layers.experimental.preprocessing.RandomFlip('horizontal'),
    layers.experimental.preprocessing.RandomRotation(0.1)
])
"""
# Model
"""
# Function for model 
def model():
    inputs = Input(shape=(32,32,1))
    # Data Augmentation
    x = data_augmentation(inputs)
    
    # Entry block
    x = layers.experimental.preprocessing.Rescaling(1./255)(x)
    x = layers.Conv2D(32,(3,3),(2,2),padding='same')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)
    x = layers.Conv2D(64,(3,3),(2,2),padding='same')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)
    
    residual1 = x
    
    for FilterSize in [128,256,512]:
        x = layers.Activation('relu')(x)
        x = layers.SeparableConv2D(FilterSize,(3,3),(2,2),padding='same')(x)
        x = layers.BatchNormalization()(x)
        x = layers.Activation(tf.nn.relu)(x)
        
        x = layers.SeparableConv2D(FilterSize,(3,3),(2,2),padding='same')(x)
        x = layers.BatchNormalization()(x)
        x = layers.Activation(tf.nn.relu)(x)
        
        x = layers.MaxPool2D((3,3),(2,2),padding='same')(x)
        
        residual = layers.Conv2D(FilterSize,(1,1),(2,2),padding='same')(residual1)
        
        x = layers.add([x,residual])
        
        FinalResidual = x
    
    x = layers.SeparableConv2D(1024,(3,3),(2,2),padding='same')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)
    
    x = layers.Flatten()(x)
    
    x = layers.Dropout(0.5)(x)
    
    output = layers.Dense(3,activation=tf.nn.softmax)(x)
    
    return Model(inputs = inputs,outputs=output)

# Intialize the model
model = model()

# Plot the model architecture
plot_model(model,show_shapes =True)
"""
## Training the model
"""
epochs = 200

callbacks = [  
    ModelCheckpoint(filepath = 'save_at_{epoch}.h5',save_best_only=True),
    EarlyStopping(monitor='val_loss',patience=20)
]

model.compile(optimizer=optimizers.SGD(0.01,momentum=0.9),
              loss='categorical_crossentropy',
              metrics=['accuracy'])
# CategoricalCrossentropy()

history = model.fit(X_train,y_train,
          batch_size=32,
          validation_batch_size=8,
          validation_data=(X_val,y_val),
          callbacks=callbacks,epochs=epochs)
# History plots
plt.figure(figsize=(10,7))
plt.plot(history.history['loss'],'b')
plt.plot(history.history['val_loss'],'r')
plt.legend(labels = ['loss','val_loss'])
plt.title('LOSS',fontsize=20)
plt.show()
plt.figure(figsize=(10,7))
plt.plot(history.history['accuracy'],'b')
plt.plot(history.history['val_accuracy'],'r')
plt.legend(labels = ['loss','val_loss'])
plt.title('LOSS',fontsize=20)
plt.show()
"""
# Test Data
"""
# Test data
test_data['loc'] = test_dir+test_data["ID"]
test_data1 = test_data['loc'].copy()
test_data2 = []
for image in test_data1:
    img = cv2.imread(image,flags=0)
    img = cv2.resize(img,(32,32))
    test_data2.append(img.astype('float32'))

test_data2 = np.stack(test_data2)    
"""
## Predictions on train dataset
"""
#### Train set
# loc = int(input('Enter the number-'))
loc = 0
# test_img = test_data2[loc]
test_img = train_x[loc]
plt.imshow(test_img)

img_array = tf.keras.preprocessing.image.img_to_array(test_img)
img_array = tf.expand_dims(img_array,0)
img_array.shape
predicted = model.predict(img_array)



def caption(pred):
    cap = np.argmax(pred)
    if cap==0:
        return 'Young'
    elif cap==1:
        return 'Middle'
    elif cap == 2:
        return 'Old'# Young=0, Middle=1,Old=2
    
cap = caption(predicted[0])
print(predicted[0])
plt.title(cap)
plt.show()
"""
## Predictions on test Data set
"""
# Test set
# loc = int(input('Enter the number-'))
loc = 0
test_img = test_data2[loc]
plt.imshow(test_img)

img_array = tf.keras.preprocessing.image.img_to_array(test_img)
img_array = tf.expand_dims(img_array,0)
img_array.shape
predicted = model.predict(img_array)


# Getting the predicted captions of the Image
def caption(pred):
    cap = np.argmax(pred)
    if cap==0:
        return 'Young'
    elif cap==1:
        return 'Middle'
    elif cap == 2:
        return 'Old'# Young=0, Middle=1,Old=2
    
cap = caption(predicted[0])
print(predicted[0])
plt.title(cap)
plt.show()