"""
# 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
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        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
"""
import json, sys, random, os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import PIL
import seaborn as sns
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Activation
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Conv2D, MaxPooling2D,ZeroPadding2D
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.callbacks import ReduceLROnPlateau, EarlyStopping, ModelCheckpoint, LearningRateScheduler
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras import backend as K
from tensorflow.keras.models import Model, load_model
from tensorflow.keras.initializers import glorot_uniform
from tensorflow.keras.utils import plot_model
from PIL import Image, ImageDraw 
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
import tensorflow as tf
from keras.preprocessing.image import ImageDataGenerator
import cv2
import matplotlib.pyplot as plt
from os import listdir
import time  
import math
import shutil
from tqdm import tqdm
from tensorflow.keras.preprocessing import image

"""
## 1.DATA PREPROCESSING AND DATA ANALYSIS
"""
train_datagen = ImageDataGenerator(
        rescale=1./255,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True)
val_datagen = ImageDataGenerator(rescale = 1./255)

test_datagen = ImageDataGenerator(rescale = 1./255)
train = train_datagen.flow_from_directory(
        '../input/brats-2019-traintestvalid/dataset/train',
        target_size=(400, 400),
        batch_size=32,
        class_mode='binary')
test = test_datagen.flow_from_directory(
        '../input/brats-2019-traintestvalid/dataset/test',
        target_size=(400, 400),
        batch_size=32,
        class_mode='binary')
validation = val_datagen.flow_from_directory(
        '../input/brats-2019-traintestvalid/dataset/valid',
        target_size=(400, 400),
        batch_size=32,
        class_mode='binary')
print(train.class_indices)
print(test.class_indices)
print(validation.class_indices)
plt.imshow(plt.imread("../input/brats-2019-traintestvalid/dataset/test/N1.jpeg"))
!pip install imutils
IMG_SIZE = (224,224)
import imutils
img = cv2.imread('../input/brats-2019-traintestvalid/dataset/test/N2.jpeg')
img = cv2.resize(
            img,
            dsize=IMG_SIZE,
            interpolation=cv2.INTER_CUBIC
        )
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)

# threshold the image, then perform a series of erosions +
# dilations to remove any small regions of noise
thresh = cv2.threshold(gray, 45, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=2)

# find contours in thresholded image, then grab the largest one
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key=cv2.contourArea)

# find the extreme points
extLeft = tuple(c[c[:, :, 0].argmin()][0])
extRight = tuple(c[c[:, :, 0].argmax()][0])
extTop = tuple(c[c[:, :, 1].argmin()][0])
extBot = tuple(c[c[:, :, 1].argmax()][0])

# add contour on the image
img_cnt = cv2.drawContours(img.copy(), [c], -1, (0, 255, 255), 4)

# add extreme points
img_pnt = cv2.circle(img_cnt.copy(), extLeft, 8, (0, 0, 255), -1)
img_pnt = cv2.circle(img_pnt, extRight, 8, (0, 255, 0), -1)
img_pnt = cv2.circle(img_pnt, extTop, 8, (255, 0, 0), -1)
img_pnt = cv2.circle(img_pnt, extBot, 8, (255, 255, 0), -1)

# crop
ADD_PIXELS = 0
new_img = img[extTop[1]-ADD_PIXELS:extBot[1]+ADD_PIXELS, extLeft[0]-ADD_PIXELS:extRight[0]+ADD_PIXELS].copy()
plt.figure(figsize=(15,6))
plt.subplot(141)
plt.imshow(img)
plt.xticks([])
plt.yticks([])
plt.title('Step 1. Get the original image')
plt.subplot(142)
plt.imshow(img_cnt)
plt.xticks([])
plt.yticks([])
plt.title('Step 2. Find the biggest contour')
plt.subplot(143)
plt.imshow(img_pnt)
plt.xticks([])
plt.yticks([])
plt.title('Step 3. Find the extreme points')
plt.subplot(144)
plt.imshow(new_img)
plt.xticks([])
plt.yticks([])
plt.title('Step 4. Crop the image')
plt.show()
"""
## 2, BUILDING THE MODEL
"""
# early stopping
es = EarlyStopping(monitor='val_accuracy', min_delta= 0.01 , patience= 5, verbose= 1, mode='auto')
model = Sequential() 

model.add(Conv2D(filters=32, kernel_size= (3,3), activation= 'relu', input_shape=(400, 400,3)) )

model.add(Conv2D(filters=32, kernel_size=(3,3), activation='relu' ))
model.add(MaxPooling2D(pool_size=(2,2)))


model.add(Conv2D(filters=64, kernel_size=(3,3), activation='relu' ))
model.add(MaxPooling2D(pool_size=(2,2)))


model.add(Conv2D(filters=128, kernel_size=(3,3), activation='relu' ))
model.add(MaxPooling2D(pool_size=(2,2)))


model.add(Flatten())
model.add(Dense(units=512, activation='relu'))
model.add(Dense(units=64, activation='relu'))
model.add(Dense(units=1, activation='sigmoid'))

model.compile(loss= "binary_crossentropy", optimizer='adam', metrics=['accuracy'] )
 
model.summary()
es = EarlyStopping(monitor='val_accuracy',min_delta= 0.01 ,  patience= 2, verbose= 2, mode='auto')
mc = ModelCheckpoint(filepath="../kaggle/working/best_model.h5",monitor='val_accuracy',save_best_only = True )
model.fit(x=train,validation_data=validation,epochs=5,callbacks = [mc,es], steps_per_epoch=50)
pd.DataFrame(model.history.history)
import seaborn as sns
sns.set_style("darkgrid")
plt.figure(figsize=(12,10))
pd.DataFrame(model.history.history).plot(figsize=(15,8))
def predictor(location):
    test_image=image.load_img(location,target_size=(400,400))
    test_image=image.img_to_array(test_image)
    test_image=np.expand_dims(test_image, axis=0)
    result=model.predict(test_image)

    if result[0][0] == 0:
        
        prediction = "The MRI image is no of BRAIN TUMOR"
    else:
        prediction = "The MRI image is of BRAIN TUMOR"
    print(result[0][0])
    return prediction
plt.imshow(plt.imread("../input/brats-2019-traintestvalid/dataset/valid/yes/y1400.jpg"))
predictor("../input/brats-2019-traintestvalid/dataset/valid/yes/y1400.jpg")
import PIL
fig, axs = plt.subplots(2, 5, figsize=(20, 10))
lst = ["../input/brats-2019-traintestvalid/dataset/valid/yes/y1401.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/yes/y1402.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/yes/y1403.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/yes/y1404.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/yes/y1405.jpg",
       "../input/brats-2019-traintestvalid/dataset/valid/no/no1400.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/no/no1401.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/no/no1402.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/no/no1403.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/no/no1404.jpg"]

axs[0][0].title.set_text(predictor(lst[0]))
axs[0][0].imshow(PIL.Image.open(lst[0]))
axs[0][1].title.set_text(predictor(lst[1]))
axs[0][1].imshow(PIL.Image.open(lst[1])) 
axs[0][2].title.set_text(predictor(lst[2]))
axs[0][2].imshow(PIL.Image.open(lst[2]))  
axs[0][3].title.set_text(predictor(lst[3]))
axs[0][3].imshow(PIL.Image.open(lst[3]))  
axs[0][4].title.set_text(predictor(lst[4]))
axs[0][4].imshow(PIL.Image.open(lst[4]))    
axs[1][0].title.set_text(predictor(lst[5]))
axs[1][0].imshow(PIL.Image.open(lst[5]))   
axs[1][1].title.set_text(predictor(lst[6]))
axs[1][1].imshow(PIL.Image.open(lst[6]))  
axs[1][2].title.set_text(predictor(lst[7]))
axs[1][2].imshow(PIL.Image.open(lst[7])) 
axs[1][3].title.set_text(predictor(lst[8]))
axs[1][3].imshow(PIL.Image.open(lst[8]))  
axs[1][4].title.set_text(predictor(lst[9]))
axs[1][4].imshow(PIL.Image.open(lst[9]))

fig.tight_layout()
"""
## 3. Transfer Learning for Better Accuracy
"""
"""
<font color="green">
Transfer learning consists of taking features learned on one problem, and leveraging them on a new, similar problem. For instance, features from a model that has learned to identify racoons may be useful to kick-start a model meant to identify tanukis.

Transfer learning is usually done for tasks where your dataset has too little data to train a full-scale model from scratch.

A pre-trained model is a saved network that was previously trained on a large dataset, typically on a large-scale image-classification task. You either use the pretrained model as is or use transfer learning to customize this model to a given task.

The intuition behind transfer learning for image classification is that if a model is trained on a large and general enough dataset, this model will effectively serve as a generic model of the visual world. You can then take advantage of these learned feature maps without having to start from scratch by training a large model on a large dataset.
First of all, we need repreprocess the images that can suit to our imported model.
    
You will follow the general machine learning workflow.

    1.Take layers from a previously trained model.

    2.Freeze them, so as to avoid destroying any of the information they contain during future training rounds.

    3.Add some new, trainable layers on top of the frozen layers. They will learn to turn the old features into predictions on a new dataset.

    4.Train the new layers on your dataset.
"""
from tensorflow.keras.applications.mobilenet import MobileNet, preprocess_input
"""
train_datagen = ImageDataGenerator(
        preprocessing_function=preprocess_input,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True)
val_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)

test_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)
train = train_datagen.flow_from_directory(
        '../input/brats-2019-traintestvalid/dataset/train',
        target_size=(224, 224),
        batch_size=32,
        class_mode='binary')
test = test_datagen.flow_from_directory(
        '../input/brats-2019-traintestvalid/dataset/test',
        target_size=(224, 224),
        batch_size=32,
        class_mode='binary')
validation = val_datagen.flow_from_directory(
        '../input/brats-2019-traintestvalid/dataset/valid',
        target_size=(224, 224),
        batch_size=32,
        class_mode='binary')
"""
train_datagen = ImageDataGenerator(
        rescale=1./255,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True)
val_datagen = ImageDataGenerator(rescale = 1./255)

test_datagen = ImageDataGenerator(rescale = 1./255)
train = train_datagen.flow_from_directory(
        '../input/brats-2019-traintestvalid/dataset/train',
        target_size=(224, 224),
        batch_size=32,
        class_mode='binary')
test = test_datagen.flow_from_directory(
        '../input/brats-2019-traintestvalid/dataset/test',
        target_size=(224, 224),
        batch_size=32,
        class_mode='binary')
validation = val_datagen.flow_from_directory(
        '../input/brats-2019-traintestvalid/dataset/valid',
        target_size=(224, 224),
        batch_size=32,
        class_mode='binary')
"""
<font color="green">
1.Take layers from a previously trained model.
"""
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
from tensorflow.keras.optimizers import RMSprop
base_model = MobileNet(input_shape=(224, 224, 3),#we can specify the inpur shape with this parameter
    include_top=False) # Do not include the ImageNet classifier at the top.)
"""
<font color="green">
2.Then, freeze the base model.

It is important to freeze the convolutional base before you compile and train the model. Freezing (by setting layer.trainable = False) prevents the weights in a given layer from being updated during training. MobileNet  has many layers, so setting the entire model's trainable flag to False will freeze all of them.
"""
base_model.trainable = False # We freeze the training of the convolutions
base_model.summary()
"""
<font color="green">
3.Add some new, trainable layers on top of the frozen layers. 
"""
# Flatten the output layer to 1 dimension
cnn = Flatten()(base_model.output)
# Add a fully connected layer with 1,024 hidden units and ReLU activation
cnn = Dense(units=1024, activation ="relu")(cnn)
# Add a dropout rate of 0.2
cnn = Dropout(0.2)(cnn)
# Add a final sigmoid layer for classification
cnn = Dense(units = 1, activation = "sigmoid")(cnn)
cnn = Model( base_model.input, cnn)
cnn.summary()
"""
<font color="green">
4.Train the new layers on your dataset.
"""
cnn.compile(optimizer = RMSprop(learning_rate=0.0001), 
              loss = 'binary_crossentropy', 
              metrics = ['accuracy'])
es = EarlyStopping(monitor='val_accuracy',min_delta= 0.01 ,  patience= 2, verbose= 2, mode='auto')
mc = ModelCheckpoint(filepath="../kaggle/working/best_modelwithtransferlearning.h5",monitor='val_accuracy',save_best_only = True )
cnn.fit(x=train,validation_data=validation,epochs=5,callbacks = [mc,es], steps_per_epoch=50)
print(cnn.evaluate(validation))
"""
<font color="green">
Our transfer model has nearly %98 accuracy compared to my own model. We saved this model during trainng and we will load this model,
"""
model_best = load_model("../kaggle/working/best_modelwithtransferlearning.h5")
model_best.evaluate(validation)
validation.class_indices
def predictor(path):
    img = image.load_img(path, target_size=(224,224),  )
    i = image.img_to_array(img)/255
    input_arr = np.array([i])
    input_arr.shape
    pred = model.predict(input_arr)[0][0] 
    if pred > 0.5:
        print("The MRI image is of BRAIN TUMOR")
    else:
        print("The MRI image is of HEALTHY BRAIN WITHOUT TUMOR")
        
"""
<font color="green">
Lets compare the predictions of both models with the test data. When we test two model with the same test data, as seen below the model with transfer learning preidict all of them correctly while the previous one make 3 of tehm fail prediction.
"""
fig, axs = plt.subplots(2, 5, figsize=(20, 10))
lst = ["../input/brats-2019-traintestvalid/dataset/valid/yes/y1401.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/yes/y1402.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/yes/y1403.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/yes/y1404.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/yes/y1405.jpg",
       "../input/brats-2019-traintestvalid/dataset/valid/no/no1400.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/no/no1401.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/no/no1402.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/no/no1403.jpg",
      "../input/brats-2019-traintestvalid/dataset/valid/no/no1404.jpg"]

axs[0][0].title.set_text(predictor(lst[0]))
axs[0][0].imshow(PIL.Image.open(lst[0]))
axs[0][1].title.set_text(predictor(lst[1]))
axs[0][1].imshow(PIL.Image.open(lst[1])) 
axs[0][2].title.set_text(predictor(lst[2]))
axs[0][2].imshow(PIL.Image.open(lst[2]))  
axs[0][3].title.set_text(predictor(lst[3]))
axs[0][3].imshow(PIL.Image.open(lst[3]))  
axs[0][4].title.set_text(predictor(lst[4]))
axs[0][4].imshow(PIL.Image.open(lst[4]))    
axs[1][0].title.set_text(predictor(lst[5]))
axs[1][0].imshow(PIL.Image.open(lst[5]))   
axs[1][1].title.set_text(predictor(lst[6]))
axs[1][1].imshow(PIL.Image.open(lst[6]))  
axs[1][2].title.set_text(predictor(lst[7]))
axs[1][2].imshow(PIL.Image.open(lst[7])) 
axs[1][3].title.set_text(predictor(lst[8]))
axs[1][3].imshow(PIL.Image.open(lst[8]))  
axs[1][4].title.set_text(predictor(lst[9]))
axs[1][4].imshow(PIL.Image.open(lst[9]))
fig.tight_layout()