{"cells":[{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"cell_type":"code","source":"import json\nimport math\nimport os\n\nimport cv2\nfrom PIL import Image\nimport numpy as np\nfrom keras import layers\nfrom keras.applications import DenseNet121\nfrom keras.callbacks import Callback, ModelCheckpoint\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.models import Sequential\nfrom keras.optimizers import Adam\nimport matplotlib.pyplot as plt\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import cohen_kappa_score, accuracy_score\nimport scipy\nimport tensorflow as tf\nfrom keras.callbacks import EarlyStopping,ReduceLROnPlateau,LearningRateScheduler\nfrom tqdm import tqdm_notebook as tqdm\n\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Set random seed for reproducibility."},{"metadata":{"trusted":true},"cell_type":"code","source":"np.random.seed(2020)\ntf.set_random_seed(2020)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Loading & Exploration"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv('../input/aptos2019-blindness-detection/train.csv')\ntest_df = pd.read_csv('../input/aptos2019-blindness-detection/test.csv')\nprint(train_df.shape)\nprint(test_df.shape)\ntrain_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df['diagnosis'].hist()\ntrain_df['diagnosis'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Displaying some Sample Images"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def display_samples(df, columns=4, rows=3):\n    fig=plt.figure(figsize=(5*columns, 4*rows))\n\n    for i in range(columns*rows):\n        image_path = df.loc[i,'id_code']\n        image_id = df.loc[i,'diagnosis']\n        img = cv2.imread(f'../input/aptos2019-blindness-detection/train_images/{image_path}.png')\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        \n        fig.add_subplot(rows, columns, i+1)\n        plt.title(image_id)\n        plt.imshow(img)\n    \n    plt.tight_layout()\n\ndisplay_samples(train_df)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Resize Images\n\nWe will resize the images to 456x456, then create a single numpy array to hold the data."},{"metadata":{"trusted":true},"cell_type":"code","source":"# image height and image width ----> GLOBAL\nimg_ht = 400\nimg_wd = 400\n\ndef displayImage(display_name, image):\n    cv2.namedWindow(display_name,cv2.WINDOW_AUTOSIZE)\n    cv2.imshow(display_name, image)\n\ndef findContourEye(thresh_image):\n    cnts = cv2.findContours(thresh_image.copy(), cv2.RETR_EXTERNAL,\n\tcv2.CHAIN_APPROX_SIMPLE)\n#     cnts = imutils.grab_contours(cnts)\n    cnts = max(cnts[0], key=cv2.contourArea)\n    return cnts\n\ndef findContourEyeExtreme(cnts):\n    # Locating extreme points on all 4 sides\n    leftmost = tuple(cnts[cnts[:,:,0].argmin()][0])\n    rightmost = tuple(cnts[cnts[:,:,0].argmax()][0])\n    topmost = tuple(cnts[cnts[:,:,1].argmin()][0])\n    bottommost = tuple(cnts[cnts[:,:,1].argmax()][0])\n    # Locating the top left and bottom right corner\n    x1 = leftmost[0]\n    y1 = topmost[1]\n    x2 = rightmost[0]\n    y2 = bottommost[1]\n    return x1,y1,x2,y2 \n\ndef findRadiusAndCentreOfContourEye(cnts):\n    M = cv2.moments(cnts)\n    if( M[\"m00\"]==0):\n        cX, cY = 0, 0\n    else:\n        cX = int(M[\"m10\"] / M[\"m00\"])\n        cY = int(M[\"m01\"] / M[\"m00\"])\n    if(cX < cY):\n        r = cX\n    else:\n        r = cY\n    return cX,cY,r\n\ndef drawCentreOnContourEye(image,cnts,cX,cY):\n    cv2.drawContours(image, [cnts], -1, (0, 255, 0), 2)\n    cv2.circle(image, (cX, cY), 7, (255, 255, 255), -1)\n    cv2.putText(image, \"center\", (cX - 20, cY - 20),\n    cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 255, 255), 2)\n    return image\n    \ndef Radius_Reduction(img,cX,cY,r):\n    h,w,c=img.shape\n    Frame=np.zeros((h,w,c),dtype=np.uint8)\n    cv2.circle(Frame,(int(cX),int(cY)),int(r), (255,255,255), -1)\n    Frame1=cv2.cvtColor(Frame, cv2.COLOR_BGR2GRAY)\n    img1 =cv2.bitwise_and(img,img,mask=Frame1)\n    return img1\n\ndef imageResize(image, ht, wd):\n    # resized_image = imutils.resize(image, height = ht, width = wd)\n    resized_image = cv2.resize(image,(wd,ht))\n    return resized_image\n\ndef crop_black(image):\n    org = image.copy()\n    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n    blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n    thresh = cv2.threshold(blurred, 10, 255, cv2.THRESH_BINARY)[1]\n    # displayImage('thresh',thresh)\n    cnts = findContourEye(thresh)\n    x1,y1,x2,y2 = findContourEyeExtreme(cnts)\n#     print(x1,y1,x2,y2)\n#     crop = org[y1:y2, x1:x2]\n#     crop = imageResize(crop, img_ht, img_wd)\n#     # displayImage(\"cr1\",crop)\n#     return crop\n    ext_x = int((x2 - x1)*4//100)\n    ext_y = int((y2 - y1)*5//100)\n#     print(ext_x,ext_y)\n    crop = org[y1+ext_y:y2-ext_y, x1+ext_x:x2-ext_x]\n    crop = imageResize(crop, img_ht, img_wd)\n    return crop\n\ndef imageAugmentation(image):\n    x_flip = cv2.flip( image, 0 )\n    y_flip = cv2.flip( image, 1 )\n    xy_flip = cv2.flip(x_flip,1)\n    return x_flip, y_flip, xy_flip\n\ndef imageHistEqualization(image):\n    lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)\n    l, a, b = cv2.split(lab)\n    clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))\n    cl = clahe.apply(l)\n    limg = cv2.merge((cl,a,b))\n    final = cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)\n    return final\n\ndef subtract_median_bg_image(im):\n    k = np.max(im.shape)//20*2+1\n    bg = cv2.medianBlur(im, k)\n    sub_med = cv2.addWeighted (im, 1, bg, -1, 255)\n    return sub_med\n\ndef colorEnhancement(image1,image2):\n    image_final = cv2.bitwise_and(image1,image2)\n    return image_final\n\ndef imageAugSave(path,img1,img2,img3,img4,img_ht,img_wd):\n    count = len(os.listdir(path))\n\n    img1 = imageResize(img1, img_ht, img_wd)\n    img2 = imageResize(img2, img_ht, img_wd)\n    img3 = imageResize(img3, img_ht, img_wd)\n    img4 = imageResize(img4, img_ht, img_wd)\n\n    cv2.imwrite(os.path.join(path , '%d.png'%(count+1)), img1)\n    cv2.imwrite(os.path.join(path , '%d.png'%(count+2)), img2)\n    cv2.imwrite(os.path.join(path , '%d.png'%(count+3)), img3)\n    cv2.imwrite(os.path.join(path , '%d.png'%(count+4)), img4)\n    return count+1,count+2,count+3,count+4\n\ndef processed_test_save(path,img,img_ht,img_wd):\n    count = len(os.listdir(path))\n    img = imageResize(img,img_ht,img_wd)\n    cv2.imwrite(os.path.join(path , '%d.png'%(count+1)), img)\n    return count+1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_pad_width(im, new_shape, is_rgb=True):\n    pad_diff = new_shape - im.shape[0], new_shape - im.shape[1]\n    t, b = math.floor(pad_diff[0]/2), math.ceil(pad_diff[0]/2)\n    l, r = math.floor(pad_diff[1]/2), math.ceil(pad_diff[1]/2)\n    if is_rgb:\n        pad_width = ((t,b), (l,r), (0, 0))\n    else:\n        pad_width = ((t,b), (l,r))\n    return pad_width\n\ndef preprocess_image(image_path, desired_size=400):\n    image = cv2.imread(image_path)\n    image = imageResize(image, desired_size, desired_size)\n    image_crop = crop_black(image)\n    image_clahe = imageHistEqualization(image_crop)\n    sub_med = subtract_median_bg_image(image_clahe)\n    image_final = colorEnhancement(sub_med, image_clahe)\n    return image_final\n\n# def preprocess_image(image_path, desired_size=380):\n#     im = Image.open(image_path)\n#     im = im.resize((desired_size, )*2, resample=Image.LANCZOS)\n    \n#     return im","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def display_samples(df, columns=4, rows=3):\n    fig=plt.figure(figsize=(5*columns, 4*rows))\n\n    for i in range(columns*rows):\n        image_path = df.loc[i,'id_code']\n        image_id = df.loc[i,'diagnosis']\n        img = preprocess_image(f'../input/aptos2019-blindness-detection/train_images/{image_path}.png')\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        \n        fig.add_subplot(rows, columns, i+1)\n        plt.title(image_id)\n        plt.imshow(img)\n    \n    plt.tight_layout()\n\ndisplay_samples(train_df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"N = train_df.shape[0]\nx_train = np.empty((N, img_ht, img_wd, 3), dtype=np.uint8)\n\nfor i, image_id in enumerate(tqdm(train_df['id_code'])):\n    x_train[i, :, :, :] = preprocess_image(\n        f'../input/aptos2019-blindness-detection/train_images/{image_id}.png'\n    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_train = pd.get_dummies(train_df['diagnosis']).values\n\nprint(x_train.shape)\nprint(y_train.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Creating multilabels\n\nInstead of predicting a single label, we will change our target to be a multilabel problem; i.e., if the target is a certain class, then it encompasses all the classes before it. E.g. encoding a class 4 retinopathy would usually be `[0, 0, 0, 1]`, but in our case we will predict `[1, 1, 1, 1]`. For more details, please check out [Lex's kernel](https://www.kaggle.com/lextoumbourou/blindness-detection-resnet34-ordinal-targets)."},{"metadata":{"trusted":true},"cell_type":"code","source":"y_train_multi = np.empty(y_train.shape, dtype=y_train.dtype)\ny_train_multi[:, 4] = y_train[:, 4]\n\nfor i in range(3, -1, -1):\n    y_train_multi[:, i] = np.logical_or(y_train[:, i], y_train_multi[:, i+1])\n\nprint(\"Original y_train:\", y_train.sum(axis=0))\nprint(\"Multilabel version:\", y_train_multi.sum(axis=0))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Now we can split it into a training and validation set."},{"metadata":{"trusted":true},"cell_type":"code","source":"x_train, x_val, y_train, y_val = train_test_split(\n    x_train, y_train_multi, \n    test_size=0.15, \n    random_state=2019\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#  Data Generator"},{"metadata":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE = 4\n\ndef create_datagen():\n    return ImageDataGenerator(\n        zoom_range=0.15,  # set range for random zoom\n        # set mode for filling points outside the input boundaries\n        fill_mode='constant',\n        cval=0.,  # value used for fill_mode = \"constant\"\n        horizontal_flip=True,  # randomly flip images\n        vertical_flip=True,  # randomly flip images\n        width_shift_range = 0.3,\n        height_shift_range=0.3\n    )\n\n# Using original generator\ndata_generator = create_datagen().flow(x_train, y_train, batch_size=BATCH_SIZE, seed=2020)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Creating keras callback for QWK"},{"metadata":{"trusted":true},"cell_type":"code","source":"class Metrics(Callback):\n    def on_train_begin(self, logs={}):\n        self.val_kappas = []\n\n    def on_epoch_end(self, epoch, logs={}):\n        X_val, y_val = self.validation_data[:2]\n        y_val = y_val.sum(axis=1) - 1\n        \n        y_pred = self.model.predict(X_val) > 0.5\n        y_pred = y_pred.astype(int).sum(axis=1) - 1\n\n        _val_kappa = cohen_kappa_score(\n            y_val,\n            y_pred, \n            weights='quadratic'\n        )\n\n        self.val_kappas.append(_val_kappa)\n\n        print(f\"val_kappa: {_val_kappa:.4f}\")\n        \n        if _val_kappa == max(self.val_kappas):\n            print(\"Validation Kappa has improved. Saving model.\")\n            self.model.save('model.h5')\n\n        return","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model: DenseNet-121"},{"metadata":{"trusted":true},"cell_type":"code","source":"# densenet = DenseNet121(\n#     weights='../input/densenet-keras/DenseNet-BC-121-32-no-top.h5',\n#     include_top=False,\n#     input_shape=(224,224,3)\n# )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Efficienetnet"},{"metadata":{"trusted":true},"cell_type":"code","source":"import gc\ngc.collect()\nimport os\nimport sys\n# Repository source: https://github.com/qubvel/efficientnet\nsys.path.append(os.path.abspath('../input/efficientnet/efficientnet-master/efficientnet-master/'))\nfrom efficientnet import EfficientNetB5\n# Load in EfficientNetB5\neffnet = EfficientNetB5(weights=None,\n                        include_top=False,\n                        input_shape=(img_wd, img_ht, 3))\neffnet.load_weights('../input/efficientnet-keras-weights-b0b5/efficientnet-b5_imagenet_1000_notop.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_model():\n    model = Sequential()\n    model.add(effnet)\n    model.add(layers.GlobalAveragePooling2D())\n    model.add(layers.Dropout(0.5))\n    model.add(layers.Dense(5, activation='sigmoid'))\n    \n    model.compile(\n        loss='binary_crossentropy',\n        optimizer=Adam(lr=0.00005),\n        metrics=['accuracy']\n    )\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = build_model()\n# model.load_weights('../input/m1-effnetb5-using-ram-fork1-3a0430/model.h5')\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training & Evaluation"},{"metadata":{"trusted":true},"cell_type":"code","source":"es = EarlyStopping(monitor='val_loss',\n                                      mode='auto',\n                                      verbose=1,\n                                      patience=10)\n\nlearning_rate_reduction = ReduceLROnPlateau(monitor='val_loss',\n                                            patience=3,\n                                            verbose=1,\n                                            mode = 'auto',\n                                            factor=0.25,\n                                            min_lr=0.000001)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"kappa_metrics = Metrics()\n\nhistory = model.fit_generator(\n    data_generator,\n    steps_per_epoch=x_train.shape[0] / BATCH_SIZE,\n    epochs=20,\n    validation_data=(x_val, y_val),\n    callbacks=[kappa_metrics,es, learning_rate_reduction]\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with open('history.json', 'w') as f:\n    try:\n        json.dump(history.history, f)\n    except:\n        pass\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history_df = pd.DataFrame(history.history)\nhistory_df[['loss', 'val_loss']].plot()\nhistory_df[['acc', 'val_acc']].plot()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(kappa_metrics.val_kappas)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.6"}},"nbformat":4,"nbformat_minor":4}